diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..95c4245c1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,227 @@ +name: "šŸ› Bug Report" +description: Only if something isn't working as expected šŸ¤”, otherwise please open a discussion. +title: "[Bug]: " + +body: + - type: markdown + attributes: + value: | + **Thanks for reporting an issue!** + + **Issues are expected to lead to changes in the repository, like code or documentation improvements or bug fixes.** + If you have handling problems or questions, consider to post in [Discussions](../discussions) instead. + + # Environment + + - type: dropdown + id: Board + attributes: + label: The type of board you are using. + options: + - Arduino ATmega328* board (Uno, Nano, etc.) + - Arduino ATmega2560 board (Mega) + - Arduino ATmega32U4 board (Leonardo, etc.) + - Arduino ATmega4809 megaAVR board (NanoEvery) + - Arduino SAM board (Due) + - Arduino SAMD board (Zero, MKR*) + - ATtiny85 board (ATTinyCore by Spence Conde) + - Digispark board + - ATtiny3217 board (TinyCore) + - ESP32 board - first check https://github.com/crankyoldgit/IRremoteESP8266 + - STM32F1 board (BluePill) + - Teensy board + - Other - please specify below + validations: + required: true + + - type: input + id: Other_board_NAME_FQBN + attributes: + label: The boards name or FQBN (e.g. esp8266:esp8266:d1_mini:eesz=4M3M,xtal=80) for unspecified board. + validations: + required: false + + - type: input + id: Other_board_URL + attributes: + label: The board manager URL used for unspecified board. + value: "https://" + validations: + required: false + + - type: dropdown + id: IDE + attributes: + label: What IDE are you using? + options: + - Arduino IDE + - Arduino Web Editor + - Arduino Pro IDE + - Sloeber IDE + - PlatformIO IDE + - Other - please specify below + multiple: false + validations: + required: true + + - type: dropdown + id: IR_Protocol + attributes: + label: What IR protocol are you using? + options: + - Unknown + - BoseWave + - Denon + - Dish + - FAST + - JVC + - Lego + - LG + - NEC + - Panasonic + - RC5, RC6 + - Samsung + - Sharp + - Sony + - Whynter + - Other - please specify below + multiple: true + validations: + required: true + + - type: input + id: IR_Pin + attributes: + label: Pin(s) used for IR-receive, if not default. + value: "Arduino pin number: default" + validations: + required: false + + - type: checkboxes + id: Checked_examples + attributes: + label: Example(s) you have checked while hunting the bug. + description: Please do not submit a bug report without testing at least one or more examples! + options: + - label: SimpleReceiver + - label: ReceiveDemo + - label: SendRawDemo + - label: ReceiverTimingAnalysis + - label: TinyReceiver + - label: TinySender + - label: ReceiveAndSend + - label: SimpleSender + - label: SendDemo + - label: SendLGAirConditionerDemo + - label: UnitTest + - label: Other - please specify below + - label: I checked, if at least one of the examples was working. + required: true + + - type: checkboxes + id: Failed_examples + attributes: + label: Example(s) to reproduce the issue. + description: Select only the examples which shows your error. + options: + - label: SimpleReceiver + - label: ReceiveDemo + - label: SendRawDemo + - label: ReceiverTimingAnalysis + - label: TinyReceiver + - label: TinySender + - label: ReceiveAndSend + - label: SimpleSender + - label: SendDemo + - label: SendLGAirConditionerDemo + - label: UnitTest + - label: Other - please specify below + + - type: checkboxes + id: Version + attributes: + label: The library version you are working with. + description: | + You may use the latest repo version, which is not (yet) released, but may contain some bugfixes. + Your current library version number can be found with *Arduino > Tools > Manage Libraries..* (Ctrl+Shift+I) + and is printed to serial output at the startup of every example. + options: + - label: I use the [latest Arduino library version](../releases) and verified this! + - label: I use the latest repo version ([download link](../archive/master.zip)) and verified this! + + - type: markdown + attributes: + value: | + # Bug descripion + + - type: textarea + id: bug-steps + attributes: + label: What are the steps to reproduce this issue? + placeholder: | + 1. + 2. + 3. + 4. See error + validations: + required: true + + - type: textarea + id: bug-description + attributes: + label: What happens? + description: A clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + id: bug-logs + attributes: + label: The serial output which indicates the error happened. + description: | + Please copy and paste any relevant serial output. This will be automatically formatted into code, so no need for backticks. + render: shell + validations: + required: false + + - type: textarea + id: bug-expectation + attributes: + label: What were you expecting to happen? + description: A clear and concise description of what was supposed to happen. + validations: + required: false + + - type: textarea + id: bug-context + attributes: + label: Additional context. + description: Add any other context you can provide about the problem here. + validations: + required: false + + - type: markdown + attributes: + value: | + # Checklist + + - type: checkboxes + id: Commitments + attributes: + label: Final checklist for the bug report. + options: + - label: I have **read** the README.md file thoroughly + required: true + - label: I have searched existing issues to see if there is anything I have missed. + required: true + - label: I have browsed the examples for one, that matches my use case. + required: true + - label: The title of the issue is helpful and relevant. + required: true + + - type: markdown + attributes: + value: | + ** We will start to close or delete issues that do not follow these guidelines + as it doesn't help the contributors who spend time trying to solve issues if the community ignores guidelines!** + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..3ba13e0ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index 952df36a6..000000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,52 +0,0 @@ -### Board -* [ ] Arduino ATmega328* board (UNO, Nano) -* [ ] Arduino ATmega2560 board (Mega) -* [ ] Arduino ATmega32U4 board (Leonardo) -* [ ] ATtiny85 board (ATTinyCore by Spence Conde) -* [ ] Digispark board -* [ ] Arduino SAM board (Due) -* [ ] Arduino SAMD board (Zero, MKR*) -* [ ] ESP32 board - first check https://github.com/crankyoldgit/IRremoteESP8266 -* [ ] Teensy board -* [ ] Other - please specify - -### Protocol -* [ ] Unknown -* [ ] BoseWave -* [ ] Denon -* [ ] Dish -* [ ] JVC -* [ ] Lego -* [ ] LG -* [ ] NEC -* [ ] Panasonic -* [ ] RC5, RC6 -* [ ] Samsung -* [ ] Sanyo -* [ ] Sharp -* [ ] Sony -* [ ] Whynter -* [ ] Other - please specify - - -**Code Block:** -``` - -#include - -..... - -``` - -Use [a gist](gist.github.com) if the code exceeds 30 lines - -**checklist:** -- [] I have **read** the README.md file thoroughly -- [] I have searched existing issues to see if there is anything I have missed. -- [] The latest [release](https://github.com/z3t0/Arduino-IRremote/releases/latest) is used -- [] Any code referenced is provided and if over 30 lines a gist is linked INSTEAD of it being pasted in here -- [] The title of the issue is helpful and relevant - -** We will start to close issues that do not follow these guidelines as it doesn't help the contributors who spend time trying to solve issues if the community ignores guidelines!** - -The above is a short template allowing you to make detailed issues! diff --git a/.github/workflows/LibraryBuild.yml b/.github/workflows/LibraryBuild.yml index 508321bb2..93aee1426 100644 --- a/.github/workflows/LibraryBuild.yml +++ b/.github/workflows/LibraryBuild.yml @@ -1,7 +1,7 @@ -# LibraryBuild.yml +# https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/.github/workflows/LibraryBuild.yml # Github workflow script to test compile all examples of an Arduino library repository. # -# Copyright (C) 2020 Armin Joachimsmeyer +# Copyright (C) 2020-2023 Armin Joachimsmeyer # https://github.com/ArminJo/Github-Actions # # Before being able to push to my .github\workflows directories, @@ -9,17 +9,21 @@ # This is the name of the workflow, visible on GitHub UI. name: LibraryBuild -on: +on: + workflow_dispatch: # To run it manually + description: 'manual build check' push: # see: https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request paths: - '**.ino' - '**.cpp' + - '**.hpp' - '**.h' - '**LibraryBuild.yml' pull_request: paths: - '**.ino' - '**.cpp' + - '**.hpp' - '**.h' - '**LibraryBuild.yml' @@ -27,11 +31,7 @@ jobs: build: name: ${{ matrix.arduino-boards-fqbn }} - test compiling examples - runs-on: ubuntu-18.04 # I picked Ubuntu to use shell scripts. - - env: - # Comma separated list without double quotes around the list. - REQUIRED_LIBRARIES: Keyboard + runs-on: ubuntu-22.04 # I picked Ubuntu to use shell scripts. strategy: matrix: @@ -40,85 +40,282 @@ jobs: # # Examples: arduino:avr:uno, arduino:avr:leonardo, arduino:avr:nano, arduino:avr:mega # arduino:sam:arduino_due_x, arduino:samd:arduino_zero_native" - # ATTinyCore:avr:attinyx5:chip=85,clock=1internal, digistump:avr:digispark-tiny, digistump:avr:digispark-pro - # STM32:stm32:GenF1:pnum=BLUEPILL_F103C8 + # ATTinyCore:avr:attinyx5:chip=85,clock=1internal + # STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103C8 # esp8266:esp8266:huzzah:eesz=4M3M,xtal=80, esp32:esp32:featheresp32:FlashFreq=80 # You may add a suffix behind the fqbn with "|" to specify one board for e.g. different compile options like arduino:avr:uno|trace ############################################################################################################# arduino-boards-fqbn: - arduino:avr:uno - - arduino:avr:uno|USE_NEC_STANDARD + - arduino:avr:uno|DEBUG_TRACE - arduino:avr:uno|USE_NO_SEND_PWM + - arduino:avr:uno|SEND_PWM_BY_TIMER + - arduino:avr:uno|USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN + - arduino:avr:mega:cpu=atmega2560 - arduino:avr:leonardo - arduino:megaavr:nona4809:mode=off - - digistump:avr:digispark-tiny:clock=clock1 - - ATTinyCore:avr:attinyx5:chip=85,clock=1internal - arduino:samd:arduino_zero_native - - esp32:esp32:featheresp32:FlashFreq=80 + - arduino:renesas_uno:unor4wifi + - adafruit:samd:adafruit_metro_m4:cache=on,speed=120,opt=small,maxqspi=50,usbstack=arduino,debug=off + - adafruit:samd:adafruit_itsybitsy_m4 + - arduino:mbed:nano33ble + - arduino:mbed_rp2040:pico + - rp2040:rp2040:arduino_nano_connect + - ATTinyCore:avr:attinyx5micr:LTO=enable,sketchclock=16pll + - ATTinyCore:avr:attinyx7micr:LTO=enable,sketchclock=16external,pinmapping=new,millis=enabled + - ATTinyCore:avr:attinyx8micr:LTO=enable,sketchclock=16external,pinmapping=mhtiny,millis=enabled # ATtiny88 China clone board @16 MHz + - MegaCore:avr:128:bootloader=no_bootloader,eeprom=keep,BOD=2v7,LTO=Os_flto,clock=8MHz_internal # ATmega128 - SparkFun:avr:promicro + - megaTinyCore:megaavr:atxy4:chip=1604,clock=16internal + - megaTinyCore:megaavr:atxy7:chip=3217,clock=16internal + - esp8266:esp8266:d1_mini:eesz=4M3M,xtal=80 + - esp32:esp32:featheresp32:FlashFreq=80 + - esp32:esp32:esp32c3 + - STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103C8 + - STMicroelectronics:stm32:GenL0:pnum=THUNDERPACK_L072 + - stm32duino:STM32F1:genericSTM32F103C - sandeepmistry:nRF5:BBCmicrobit + - Seeeduino:samd:seeed_XIAO_m0:usbstack=arduino,debug=off,sercom4=include # Specify parameters for each board. # With sketches-exclude you may exclude specific examples for a board. Use a comma separated list. ############################################################################################################# include: - arduino-boards-fqbn: arduino:avr:uno - sketches-exclude: IR2Keyboard + build-properties: # the flags were put in compiler.cpp.extra_flags + ReceiveDemo: -DIR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=1200 -DIR_SEND_PIN=3 - - arduino-boards-fqbn: arduino:avr:uno|USE_NEC_STANDARD - sketches-exclude: IR2Keyboard + - arduino-boards-fqbn: arduino:avr:uno|DEBUG_TRACE build-properties: # the flags were put in compiler.cpp.extra_flags - All: -DUSE_NEC_STANDARD + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 -DIR_SEND_PIN=3 + TinyReceiver: -DUSE_CALLBACK_FOR_TINY_RECEIVER + All: -DEBUG -DTRACE - arduino-boards-fqbn: arduino:avr:uno|USE_NO_SEND_PWM - sketches-exclude: IR2Keyboard build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 -DIR_SEND_PIN=3 + TinyReceiver: -DUSE_FAST_PROTOCOL + TinySender: -DUSE_FAST_PROTOCOL All: -DUSE_NO_SEND_PWM + - arduino-boards-fqbn: arduino:avr:uno|SEND_PWM_BY_TIMER + sketches-exclude: UnitTest,MultipleSendPins + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 -DIR_SEND_PIN=3 -DSEND_PWM_BY_TIMER + ReceiveDemo: -DDECODE_ONKYO + TinyReceiver: -DUSE_ONKYO_PROTOCOL + TinySender: -DUSE_ONKYO_PROTOCOL + All: -DSEND_PWM_BY_TIMER + + - arduino-boards-fqbn: arduino:avr:uno|USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 -DIR_SEND_PIN=3 -DUSE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN + SimpleSender: -DSEND_PWM_BY_TIMER + TinyReceiver: -DENABLE_NEC2_REPEATS + TinySender: -DENABLE_NEC2_REPEATS + All: -DUSE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN + + - arduino-boards-fqbn: arduino:avr:mega:cpu=atmega2560 + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 + SimpleSender: -DSEND_PWM_BY_TIMER + TinyReceiver: -DDISABLE_PARITY_CHECKS + TinySender: -DDISABLE_PARITY_CHECKS + + - arduino-boards-fqbn: arduino:avr:leonardo + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 + TinyReceiver: -DNO_LED_FEEDBACK_CODE + TinySender: -DNO_LED_FEEDBACK_CODE + sketches-exclude: UnitTest # Sketch uses 28764 bytes (100%) of program storage space. Maximum is 28672 bytes + - arduino-boards-fqbn: arduino:megaavr:nona4809:mode=off - sketches-exclude: IR2Keyboard + sketches-exclude: TinyReceiver,IRDispatcherDemo + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 + SimpleSender: -DSEND_PWM_BY_TIMER + TinyReceiver: -DUSE_ONKYO_PROTOCOL -DENABLE_NEC2_REPEATS -DNO_LED_FEEDBACK_CODE -DUSE_CALLBACK_FOR_TINY_RECEIVER + TinySender: -DUSE_ONKYO_PROTOCOL -DENABLE_NEC2_REPEATS -DNO_LED_FEEDBACK_CODE + + - arduino-boards-fqbn: arduino:samd:arduino_zero_native + sketches-exclude: TinyReceiver,IRDispatcherDemo + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 + + - arduino-boards-fqbn: arduino:renesas_uno:unor4wifi + sketches-exclude: TinyReceiver,IRDispatcherDemo + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 - - arduino-boards-fqbn: digistump:avr:digispark-tiny:clock=clock1 - platform-url: https://raw.githubusercontent.com/ArminJo/DigistumpArduino/master/package_digistump_index.json - sketches-exclude: IR2Keyboard,IRtest,IRtest2,IRrelay,LegoPowerFunctionsTests,IRrecord,IRreceiveDumpV2,IRsendProntoDemo,MicroGirs # Does not fit in FLASH or RAM # Comma separated list of (unique substrings of) example names to exclude in build + - arduino-boards-fqbn: adafruit:samd:adafruit_metro_m4:cache=on,speed=120,opt=small,maxqspi=50,usbstack=arduino,debug=off + platform-url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json + sketches-exclude: TinyReceiver,IRDispatcherDemo + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 + + - arduino-boards-fqbn: adafruit:samd:adafruit_itsybitsy_m4 + platform-url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json + sketches-exclude: TinyReceiver,IRDispatcherDemo + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 - - arduino-boards-fqbn: ATTinyCore:avr:attinyx5:chip=85,clock=1internal + + - arduino-boards-fqbn: arduino:mbed:nano33ble + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 + SimpleSender: -DSEND_PWM_BY_TIMER + +# +# RP2040 +# + - arduino-boards-fqbn: arduino:mbed_rp2040:pico + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 + SimpleSender: -DSEND_PWM_BY_TIMER + + - arduino-boards-fqbn: rp2040:rp2040:arduino_nano_connect + platform-url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DSEND_PWM_BY_TIMER + SimpleSender: -DSEND_PWM_BY_TIMER + All: -DRAW_BUFFER_LENGTH=750 + +# +# megaTinyCore +# + - arduino-boards-fqbn: megaTinyCore:megaavr:atxy4:chip=1604,clock=16internal + arduino-platform: megaTinyCore:megaavr platform-url: http://drazzy.com/package_drazzy.com_index.json - sketches-exclude: IR2Keyboard,IRtestIRtest2,IRrelay,LegoPowerFunctionsTests,IRrecord,IRreceiveDumpV2,IRsendProntoDemo,MicroGirs,BoseWaveSendDemo # Does not fit in FLASH or RAM + sketches-exclude: AllProtocolsOnLCD,UnitTest # UnitTest region `text' overflowed by 997 bytes + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=360 + + - arduino-boards-fqbn: megaTinyCore:megaavr:atxy7:chip=3217,clock=16internal + arduino-platform: megaTinyCore:megaavr + platform-url: http://drazzy.com/package_drazzy.com_index.json + sketches-exclude: AllProtocolsOnLCD + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=360 + +# +# ATTinyCore +# + - arduino-boards-fqbn: ATTinyCore:avr:attinyx5micr:LTO=enable,sketchclock=16pll + platform-url: http://drazzy.com/package_drazzy.com_index.json + required-libraries: ATtinySerialOut + sketch-names: TinyReceiver.ino,IRremoteInfo.ino,SimpleReceiver.ino,ReceiveDemo.ino,ControlRelay.ino,SimpleSender.ino,SendDemo.ino,SendRawDemo.ino,SendAndReceive.ino,SimpleReceiverForHashCodes.ino + build-properties: # the flags were put in compiler.cpp.extra_flags + SimpleSender: -DSEND_PWM_BY_TIMER + SimpleReceiver: -DDECODE_NEC + + - arduino-boards-fqbn: ATTinyCore:avr:attinyx7micr:LTO=enable,sketchclock=16external,pinmapping=new,millis=enabled + platform-url: http://drazzy.com/package_drazzy.com_index.json + required-libraries: ATtinySerialOut + sketch-names: TinyReceiver.ino,IRremoteInfo.ino,SimpleReceiver.ino,ReceiveDemo.ino,ControlRelay.ino,SimpleSender.ino,SendDemo.ino,SendRawDemo.ino,SendAndReceive.ino,SimpleReceiverForHashCodes.ino + build-properties: # the flags were put in compiler.cpp.extra_flags + SimpleSender: -DSEND_PWM_BY_TIMER + + - arduino-boards-fqbn: ATTinyCore:avr:attinyx8micr:LTO=enable,sketchclock=16external,pinmapping=mhtiny,millis=enabled # ATtiny88 China clone board @16 MHz + platform-url: http://drazzy.com/package_drazzy.com_index.json + required-libraries: ATtinySerialOut + sketch-names: TinyReceiver.ino,IRremoteInfo.ino,SimpleReceiver.ino,ReceiveDemo.ino,ControlRelay.ino,SimpleSender.ino,SendDemo.ino,SendRawDemo.ino,SendAndReceive.ino,SimpleReceiverForHashCodes.ino + build-properties: # the flags were put in compiler.cpp.extra_flags + SimpleSender: -DSEND_PWM_BY_TIMER + SimpleReceiver: -DDECODE_NEC + +# +# MegaCore +# + - arduino-boards-fqbn: MegaCore:avr:128:bootloader=no_bootloader,eeprom=keep,BOD=2v7,LTO=Os_flto,clock=8MHz_internal + platform-url: https://mcudude.github.io/MegaCore/package_MCUdude_MegaCore_index.json + arduino-platform: arduino:avr,MegaCore:avr # gcc is taken from arduino:avr + build-properties: # the flags were put in compiler.cpp.extra_flags + SimpleSender: -DSEND_PWM_BY_TIMER + All: -DRAW_BUFFER_LENGTH=750 + +# +# ESP +# + - arduino-boards-fqbn: esp8266:esp8266:d1_mini:eesz=4M3M,xtal=80 + platform-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json + sketches-exclude: TinyReceiver + build-properties: # the flags were put in compiler.cpp.extra_flags + All: -DRAW_BUFFER_LENGTH=750 - arduino-boards-fqbn: esp32:esp32:featheresp32:FlashFreq=80 - platform-url: https://dl.espressif.com/dl/package_esp32_index.json - sketches-exclude: IR2Keyboard,LGACSendDemo # undefined reference to `TwoWire::onReceive(void (*)(int))' + platform-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + sketches-exclude: TinyReceiver # undefined reference to `TwoWire::onReceive(void (*)(int))' + build-properties: # the flags were put in compiler.cpp.extra_flags. SEND_PWM_BY_TIMER is always enabled! + IRremoteExtensionTest: -MMD -c # see https://github.com/espressif/arduino-esp32/issues/8815 + All: -DRAW_BUFFER_LENGTH=750 -MMD -c + + - arduino-boards-fqbn: esp32:esp32:esp32c3 + platform-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + sketches-exclude: TinyReceiver # undefined reference to `TwoWire::onReceive(void (*)(int))' + build-properties: # the flags were put in compiler.cpp.extra_flags. SEND_PWM_BY_TIMER is always enabled! + IRremoteExtensionTest: -MMD -c # see https://github.com/espressif/arduino-esp32/issues/8815 + All: -DRAW_BUFFER_LENGTH=750 -MMD -c + +# +# STM +# + - arduino-boards-fqbn: STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103C8 # ST version + platform-url: https://raw.githubusercontent.com/stm32duino/BoardManagerFiles/main/package_stmicroelectronics_index.json + sketches-exclude: TinyReceiver + build-properties: # the flags were put in compiler.cpp.extra_flags + All: -DRAW_BUFFER_LENGTH=750 + - arduino-boards-fqbn: STMicroelectronics:stm32:GenL0:pnum=THUNDERPACK_L072 # ST version + platform-url: https://raw.githubusercontent.com/stm32duino/BoardManagerFiles/main/package_stmicroelectronics_index.json + sketches-exclude: TinyReceiver,IRDispatcherDemo + build-properties: # the flags were put in compiler.cpp.extra_flags + All: -DRAW_BUFFER_LENGTH=750 + + - arduino-boards-fqbn: stm32duino:STM32F1:genericSTM32F103C # Roger Clark version + platform-url: http://dan.drown.org/stm32duino/package_STM32duino_index.json + sketches-exclude: TinyReceiver + build-properties: # the flags were put in compiler.cpp.extra_flags + All: -DRAW_BUFFER_LENGTH=750 + +# +# Others +# - arduino-boards-fqbn: SparkFun:avr:promicro arduino-platform: arduino:avr,SparkFun:avr platform-url: https://raw.githubusercontent.com/sparkfun/Arduino_Boards/master/IDE_Board_Manager/package_sparkfun_index.json # Arduino URL is not required here + build-properties: # the flags were put in compiler.cpp.extra_flags. SEND_PWM_BY_TIMER is always enabled! + All: -DRAW_BUFFER_LENGTH=750 + sketches-exclude: UnitTest # Sketch uses 28762 bytes (100%) of program storage space. Maximum is 28672 bytes. - arduino-boards-fqbn: sandeepmistry:nRF5:BBCmicrobit platform-url: https://sandeepmistry.github.io/arduino-nRF5/package_nRF5_boards_index.json - sketches-exclude: IR2Keyboard,BoseWaveSendDemo,IRrecord,IRsendDemo,IRsendProntoDemo,IRsendRawDemo,IRsendNecStandardDemo,IRtest,IRtest2,LegoPowerFunctionsSendDemo,JVCPanasonicSendDemo,LGACSendDemo,MicroGirs # no sending yet + sketches-exclude: IRDispatcherDemo,MicroGirs,TinyReceiver + build-properties: # the flags were put in compiler.cpp.extra_flags + All: -DRAW_BUFFER_LENGTH=750 + - arduino-boards-fqbn: Seeeduino:samd:seeed_XIAO_m0:usbstack=arduino,debug=off,sercom4=include + platform-url: https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json + sketches-exclude: AllProtocolsOnLCD,UnitTest,ReceiveAndSendDistanceWidth,ReceiveDemo,SimpleReceiverForHashCodes # No print(uint64_t,.) available + build-properties: # the flags were put in compiler.cpp.extra_flags + All: -DRAW_BUFFER_LENGTH=750 - # Do not cancel all jobs / architectures if one job fails - fail-fast: false +# fail-fast: false # false -> do not cancel all jobs / architectures if one job fails steps: - name: Checkout uses: actions/checkout@master - # Use the arduino-test-compile script, because it is faster - - name: Compile all examples using the bash script arduino-test-compile.sh - env: - # Passing parameters to the script by setting the appropriate ENV_* variables. - ENV_ARDUINO_BOARD_FQBN: ${{ matrix.arduino-boards-fqbn }} - ENV_ARDUINO_PLATFORM: ${{ matrix.arduino-platform }} - ENV_PLATFORM_URL: ${{ matrix.platform-url }} - ENV_REQUIRED_LIBRARIES: ${{ env.REQUIRED_LIBRARIES }} - ENV_EXAMPLES_EXCLUDE: ${{ matrix.sketches-exclude }} - ENV_EXAMPLES_BUILD_PROPERTIES: ${{ toJson(matrix.build-properties) }} - - run: | - wget --quiet https://raw.githubusercontent.com/ArminJo/arduino-test-compile/master/arduino-test-compile.sh - ls -l arduino-test-compile.sh - chmod +x arduino-test-compile.sh - ./arduino-test-compile.sh + - name: Compile all examples using the arduino-test-compile action + uses: ArminJo/arduino-test-compile@master + with: + arduino-board-fqbn: ${{ matrix.arduino-boards-fqbn }} + arduino-platform: ${{ matrix.arduino-platform }} # for MegaCore + platform-url: ${{ matrix.platform-url }} + required-libraries: ${{ matrix.required-libraries }} + sketch-names: ${{ matrix.sketch-names }} # Comma separated list of sketch names (without path, but with extension) or patterns to use in build + sketches-exclude: ${{ matrix.sketches-exclude }} + build-properties: ${{ toJson(matrix.build-properties) }} +# cli-version: 0.33.0 # to avoid errors for ATTinyCore +# debug-install: true +# debug-compile: true \ No newline at end of file diff --git a/.github/workflows/PlatformIoPublish.yml b/.github/workflows/PlatformIoPublish.yml new file mode 100644 index 000000000..d8727408c --- /dev/null +++ b/.github/workflows/PlatformIoPublish.yml @@ -0,0 +1,42 @@ +# PlatformIoPublish.yml +# Github workflow script to publish a release to PlatformIo. +# +# Copyright (C) 2021-2023 Armin Joachimsmeyer +# https://github.com/ArminJo/Github-Actions +# + +# This is the name of the workflow, visible on GitHub UI. +name: PlatformIo publishing +on: + workflow_dispatch: # To run it manually + description: manual PlatformIo publishing + release: # see: https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#example-using-multiple-events-with-activity-types-or-configuration + types: + - created + +jobs: + publish: + runs-on: ubuntu-latest + steps: + + - name: Checkout + uses: actions/checkout@master + + - name: Set up Python + uses: actions/setup-python@master + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install platformio + + - name: Build and publish + env: + PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_TOKEN }} + run: | + pio package publish --owner Arduino-IRremote --non-interactive +# run: | +# pio package pack +# pio package publish --owner Arduino-IRremote --non-interactive diff --git a/Contributing.md b/Contributing.md index 40a1ce080..4424f809c 100644 --- a/Contributing.md +++ b/Contributing.md @@ -1,13 +1,47 @@ -# Contribution Guidelines +# Contributing +This library is the culmination of the expertise of many members of the open source community who have dedicated their time and hard work. -This library is the culmination of the expertise of many members of the open source community who have dedicated their time and hard work. The best way to ask for help or propose a new idea is to [create a new issue](https://github.com/z3t0/Arduino-IRremote/issues/new) while creating a Pull Request with your code changes allows you to share your own innovations with the rest of the community. +If you want to contribute to this project: +- Report bugs and errors +- Ask for enhancements +- Create issues and pull requests +- Tell other people about this library +- Contribute new protocols -The following are some guidelines to observe when creating issues or PRs: -- Be friendly; it is important that we can all enjoy a safe space as we are all working on the same project and it is okay for people to have different ideas -- [Use code blocks](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code); **it helps us help you when we can read your code!** On that note also refrain from pasting more than 30 lines of code in a post, instead **[create a gist](https://gist.github.com/) if you need to share large snippets** -- Use reasonable titles; refrain from using overly long or capitalized titles as they are usually annoying and do little to encourage others to help :smile: -- Be detailed; refrain from mentioning code problems without sharing your source code and always give information regarding your board and version of the library -- Use the style; we use the original [C Style by Kerninghan / Ritchie](https://en.wikipedia.org/wiki/Indentation_style#K&R_style) in [variant: 1TBS (OTBS)](https://en.wikipedia.org/wiki/Indentation_style#Variant:_1TBS_(OTBS)). In short: 4 spaces indentation, no tabs, opening braces on the same line, braces are mandatory on all if/while/do, no hard line length limit. To beautify your code, you may use the online formatter [here](https://www.freecodeformat.com/c-format.php). -- Choose the right Pull Request target; if you only have **minor changes or adding a new protocol**, choose **master** as target for your Pull Request. If have a change **addressing more general aspects** of this library or think, that the PR should be **discussed and reviewed**, choose the **dev** branch as target for your Pull Request like described [here](https://guides.github.com/introduction/flow/). +## Guidelines +The following are some guidelines to observe when creating discussions / PRs: +#### Be friendly +It is important that we can all enjoy a safe space as we are all working on the same project and **it is okay for people to have different ideas**. +#### Use reasonable titles +Refrain from using overly long or capitalized titles as they are usually annoying and do little to encourage others to help :smile:. +#### Use the formatting style +We use the original [C Style by Kerninghan / Ritchie](https://en.wikipedia.org/wiki/Indentation_style#K&R_style) in [variant: 1TBS (OTBS)](https://en.wikipedia.org/wiki/Indentation_style#Variant:_1TBS_(OTBS)).
+In short: 4 spaces indentation, no tabs, opening braces on the same line, braces are mandatory on all if/while/do, no hard line length limit.
+To beautify your code, you may use the online formatter [here](https://www.freecodeformat.com/c-format.php). +#### Cover **all** occurences of the problem / addition you address with your PR + Do not forget the documentation like it is done for existing code. Code changes without proper documentation will be rejected! -If there is any need to contact me then you can find my email on the README, I do not mind responding to emails but it would be in your own interests to create issues if you need help with the library as responses would be from a larger community with greater knowledge! \ No newline at end of file +## Adding new protocols +To add a new protocol is quite straightforward. Best is too look at the existing protocols to find a similar one and modify it.
+As a rule of thumb, it is easier to work with a description of the protocol rather than trying to entirely reverse-engineer the protocol. +Please include a link to the description in the header, if you found one.
+The **durations** you receive are likely to be longer for marks and shorter for spaces than the protocol suggests, +but this depends on the receiver circuit in use. Most protocols use multiples of one time-unit for marks and spaces like e.g. [NEC](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_NEC.hpp#L62). It's easy to be off-by-one with the last bit, since the last space is not recorded by IRremote. + +Try to make use of the template functions `decodePulseDistanceData()` and `sendPulseDistanceData()`. +If your protocol supports address and code fields, try to reflect this in your api like it is done in [`sendNEC(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aIsRepeat)`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_NEC.hpp#L96) +and [`decodeNEC()`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_NEC.hpp#L194).
+ +### Integration +To integrate your protocol, you need to extend the two functions `decode()` and `getProtocolString()` in *IRreceice.hpp*, +add macros and function declarations for sending and receiving and extend the `enum decode_type_t` in *IRremote.h*.
+And at least it would be wonderful if you can provide an example how to use the new protocol. +A detailed description can be found in the [ir_Template.hpp](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Template.hpp#L11) file. + +### Creating API documentation +To generate the API documentation, Doxygen, as well as [Graphviz](http://www.graphviz.org/) should be installed. +(Note that on Windows, it is useful to specify the installer to add Graphviz to PATH or to do it manually. +With Doxygen and Graphviz installed, issue the command +`doxygen` from the command line in the main project directory, which will +generate the API documentation in HTML format. +The just generated `docs/index.html` can now be opened in a browser. \ No newline at end of file diff --git a/Contributors.md b/Contributors.md index aff62f2a9..40c040464 100644 --- a/Contributors.md +++ b/Contributors.md @@ -1,7 +1,8 @@ ## Contributors These are the active contributors of this project that you may contact if there is anything you need help with or if you have suggestions. -- [z3t0](https://github.com/z3t0) and currently also the main contributor. +- [ArminJo](https://github.com/ArminJo) Maintainer +- [z3t0](https://github.com/z3t0) the main contributor until version 2.4.0. * Email: zetoslab@gmail.com - [shirriff](https://github.com/shirriff): An amazing person who worked to create this awesome library and provide unending support - [Informatic](https://github.com/Informatic) @@ -21,7 +22,15 @@ These are the active contributors of this project that you may contact if there - [MrBryonMiller](https://github.com/MrBryonMiller) - [bengtmartensson](https://github.com/bengtmartensson) providing support - [AnalysIR](https:/github.com/AnalysIR) providing support -- [ArminJo](https://github.com/ArminJo) Maintainer - [eshicks4](https://github.com/eshicks4) +- [Jim-2249](https://github.com/Jim-2249) +- [pmalasp](https://github.com/pmalasp ) +- [ElectronicsArchiver}(https://github.com/ElectronicsArchiver) improving documentation +- [Stephen Humphries](https://github.com/sjahu)Fix for: Prevent long delay caused by overflow when frame duration < repeat period #1028 +- [Daniel Wallner](https://github.com/danielwallner) Bang & Olufsen protocol. +- [slott](https://stackoverflow.com/users/11680056/sklott) Seeduino print(unsigned long long...) support. +- [Joe Ostrander](https://github.com/joeostrander) Added support for attiny1614. +- [Buzzerb](https://github.com/Buzzerb) Added Extended NEC protocol to TinyIR and making it more consistent. +- [akellai](https://github.com/akellai) Added ESP 3.0 support. -Note: Please let [z3t0](https://github.com/z3t0) know if you have been missed. +Note: Please let [ArminJo](https://github.com/ArminJo) know if you have been missed. diff --git a/Doxyfile b/Doxyfile index e91220466..a879240e6 100644 --- a/Doxyfile +++ b/Doxyfile @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = +OUTPUT_DIRECTORY = E:\WORKSPACE_ARDUINO\lib\Arduino-IRremote_gh-pages # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -307,7 +307,7 @@ OPTIMIZE_OUTPUT_SLICE = NO # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. -EXTENSION_MAPPING = ino=C++ cpp=C++ h=C++ +EXTENSION_MAPPING = ino=C++ cpp=C++ h=C++ hpp=C++ # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable @@ -815,7 +815,6 @@ WARN_LOGFILE = INPUT = src \ src/private \ - examples/MicroGirs \ . # This tag can be used to specify the character encoding of the source files @@ -843,7 +842,7 @@ INPUT_ENCODING = UTF-8 # C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, # *.vhdl, *.ucf, *.qsf and *.ice. -FILE_PATTERNS = *.ino *.cpp *.c *.h *.md +FILE_PATTERNS = *.ino *.cpp *.c *.h *.hpp *.md # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -1064,7 +1063,7 @@ VERBATIM_HEADERS = YES # classes, structs, unions or interfaces. # The default value is: YES. -ALPHABETICAL_INDEX = YES +ALPHABETICAL_INDEX = NO # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. @@ -1096,8 +1095,6 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -# This name is selected to be able to easily publish with GitHub's gh-pages. - HTML_OUTPUT = docs # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each @@ -1457,7 +1454,7 @@ DISABLE_INDEX = NO # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_TREEVIEW = NO +GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. diff --git a/LICENSE.txt b/LICENSE similarity index 86% rename from LICENSE.txt rename to LICENSE index 339275718..184ff748a 100644 --- a/LICENSE.txt +++ b/LICENSE @@ -1,9 +1,8 @@ +MIT License (c) Copyright 2009 Ken Shirriff http://www.righto.com (c) Copyright 2016 Rafi Khan -(c) Copyright 2020 Armin Joachimsmeyer et al. - -Licensed under the MIT license: +(c) Copyright 2020-2022 Armin Joachimsmeyer et al. http://www.opensource.org/licenses/mit-license.php @@ -14,13 +13,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 68bd88d26..d630db6e6 100644 --- a/README.md +++ b/README.md @@ -1,160 +1,1134 @@ -# IRremote Arduino Library -Available as Arduino library "IRremote" +
-### [Version 2.8.0](https://github.com/z3t0/Arduino-IRremote/releases) - work in progress +# Arduino IRremote +A library enabling the sending & receiving of infra-red signals. -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Commits since latest](https://img.shields.io/github/commits-since/z3t0/Arduino-IRremote/latest)](https://github.com/z3t0/Arduino-IRremote/commits/master) -[![Installation instructions](https://www.ardu-badge.com/badge/IRremote.svg?)](https://www.ardu-badge.com/IRremote) -[![Join the chat at https://gitter.im/z3t0/Arduino-IRremote](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/z3t0/Arduino-IRremote?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![LibraryBuild](https://github.com/z3t0/Arduino-IRremote/workflows/LibraryBuild/badge.svg)](https://github.com/z3t0/Arduino-IRremote/actions) +[![Badge License: MIT](https://img.shields.io/badge/License-MIT-ac8b11.svg?style=for-the-badge&labelColor=yellow)](https://opensource.org/licenses/MIT) +     +[![Badge Version](https://img.shields.io/github/v/release/Arduino-IRremote/Arduino-IRremote?include_prereleases&style=for-the-badge&color=33660e&labelColor=428813&logoColor=white&logo=DocuSign)](https://github.com/Arduino-IRremote/Arduino-IRremote/releases/latest) +     +[![Badge Commits since latest](https://img.shields.io/github/commits-since/Arduino-IRremote/Arduino-IRremote/latest?style=for-the-badge&color=004463&labelColor=00557f)](https://github.com/Arduino-IRremote/Arduino-IRremote/commits/master) +     +[![Badge LibraryBuild](https://img.shields.io/github/actions/workflow/status/Arduino-IRremote/Arduino-IRremote/LibraryBuild.yml?branch=master&style=for-the-badge&color=551f47&labelColor=752a61)](https://github.com/Arduino-IRremote/Arduino-IRremote/actions) +
+
+[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) -This library enables you to send and receive using infra-red signals on an Arduino. +Available as [Arduino library "IRremote"](https://www.arduinolibraries.info/libraries/i-rremote). -Tutorials and more information will be made available on [the official homepage](http://z3t0.github.io/Arduino-IRremote/). +[![Button Install](https://img.shields.io/badge/Install-yellow?style=for-the-badge&logoColor=white&logo=GitBook)](https://www.ardu-badge.com/IRremote) +     +[![Button API](https://img.shields.io/badge/API-1c8840?style=for-the-badge&logoColor=white&logo=OpenStreetMap)](https://arduino-irremote.github.io/Arduino-IRremote/classIRrecv.html) +     +[![Button Changelog](https://img.shields.io/badge/Changelog-00557f?style=for-the-badge&logoColor=white&logo=AzureArtifacts)](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/changelog.md) +     +[![Button Contribute](https://img.shields.io/badge/Contribute-752a61?style=for-the-badge&logoColor=white&logo=GitHub)](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/Contributing.md) -# Installation -Click on the LibraryManager badge above to see the instructions. +#### If you find this program useful, please give it a star. -# FAQ -- IR does not work right when I use Neopixels (aka WS2811/WS2812/WS2812B) -Whether you use the Adafruit Neopixel lib, or FastLED, interrupts get disabled on many lower end CPUs like the basic arduinos for longer than 50 us. In turn, this stops the IR interrupt handler from running when it needs to. There are some solutions to this on some processors, [see this page from Marc MERLIN](http://marc.merlins.org/perso/arduino/post_2017-04-03_Arduino-328P-Uno-Teensy3_1-ESP8266-ESP32-IR-and-Neopixels.html) -- The default IR timer on AVR's is timer 2. Since the Arduino Tone library as well as analogWrite() for pin 3 and pin 11 requires timer 2, this functionality cannot be used simultaneously. -- You can use **multiple IR receiver** by just connecting the output pins of several IR receivers together. The IR receivers use an NPN transistor as output device with just a 30k resistor to VCC. This is almost "open collector" and allows connecting of several output pins to one Arduino input pin. +🌎 [Google Translate](https://translate.google.com/translate?sl=en&u=https://github.com/Arduino-IRremote/Arduino-IRremote) + +
+ +# Table of content +- [Supported IR Protocols](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#supported-ir-protocols) +- [Common problem with IRremote](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#common-problem-with-irremote) +- [Using the new library version for old examples](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#using-the-new-library-version-for-old-examples) + * [New features of version 4.x](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#new-features-of-version-4x) + * [New features of version 3.x](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#new-features-of-version-3x) + * [Converting your 2.x program to the 4.x version](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#converting-your-2x-program-to-the-4x-version) + * [How to convert old MSB first 32 bit IR data codes to new LSB first 32 bit IR data codes](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-to-convert-old-msb-first-32-bit-ir-data-codes-to-new-lsb-first-32-bit-ir-data-codes) + * [Errors when using the 3.x versions for old tutorials](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#errors-when-using-the-3x-versions-for-old-tutorials) + * [Staying on 2.x](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#staying-on-2x) +- [Why *.hpp instead of *.cpp](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#why-hpp-instead-of-cpp) +- [Using the new *.hpp files](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#using-the-new-hpp-files) +- [Tutorials](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tutorials) +- [3 ways to specify an IR code](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#3-ways-to-specify-an-ir-code) +- [IRReceiver pinouts](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#irreceiver-pinouts) +- [Receiving IR codes](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#receiving-ir-codes) + * [decodedIRData structure](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#decodedirdata-structure) + * [Ambiguous protocols](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#ambiguous-protocols) + * [RAM usage of different protocolsl](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#ram-usage-of-different-protocols) + * [Handling unknown Protocols](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#handling-unknown-protocols) + * [Disclaimer](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#disclaimer) + * [Other libraries, which may cover these protocols](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#other-libraries-which-may-cover-these-protocols) + * [Protocol=PULSE_DISTANCE](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#protocolpulse_distance) + * [Protocol=UNKNOWN](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#protocolunknown) + * [How to deal with protocols not supported by IRremote](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-to-deal-with-protocols-not-supported-by-irremote) +- [Sending IR codes](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#sending-ir-codes) + * [Sending IRDB IR codes](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#sending-irdb-ir-codes) + * [Send pin](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#send-pin) + + [List of public IR code databases](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#list-of-public-ir-code-databases) +- [Tiny NEC receiver and sender](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tiny-nec-receiver-and-sender) +- [The FAST protocol](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#the-fast-protocol) +- [FAQ and hints](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#faq-and-hints) + * [Receiving stops after analogWrite() or tone() or after running a motor](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#receiving-stops-after-analogwrite-or-tone-or-after-running-a-motor) + * [Receiving sets overflow flag](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#receiving-sets-overflow-flag) + * [Problems with Neopixels, FastLed etc.](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#problems-with-neopixels-fastled-etc) + * [Does not work/compile with another library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#does-not-workcompile-with-another-library) + * [Multiple IR receiver and sender instances](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#multiple-ir-receiver-and-sender-instances) + * [Increase strength of sent output signal](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#increase-strength-of-sent-output-signal) + * [Minimal CPU clock frequency](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#minimal-cpu-clock-frequency) + * [Bang & Olufsen protocol](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#bang--olufsen-protocol) +- [Examples for this library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#examples-for-this-library) +- [WOKWI online examples](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#wokwi-online-examples) +- [IR control of a robot car](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#ir-control-of-a-robot-car) +- [Issues and discussions](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#issues-and-discussions) +- [Compile options / macros for this library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library) + + [Changing include (*.h) files with Arduino IDE](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#changing-include-h-files-with-arduino-ide) + + [Modifying compile options with Sloeber IDE](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#modifying-compile-options--macros-with-sloeber-ide) +- [Supported Boards](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#supported-boards) +- [Timer and pin usage](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage) + * [Incompatibilities to other libraries and Arduino commands like tone() and analogWrite()](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#incompatibilities-to-other-libraries-and-arduino-commands-like-tone-and-analogwrite) + * [Hardware-PWM signal generation for sending](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#hardware-pwm-signal-generation-for-sending) + * [Why do we use 30% duty cycle for sending](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#why-do-we-use-30-duty-cycle-for-sending) +- [How we decode signals](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-we-decode-signals) +- [NEC encoding diagrams](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#nec-encoding-diagrams) +- [Quick comparison of 5 Arduino IR receiving libraries](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#quick-comparison-of-5-arduino-ir-receiving-libraries) +- [History](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/changelog.md) +- [Useful links](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#useful-links) +- [Contributors](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/Contributors.md) +- [License](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#license) +- [Copyright](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#copyright) + +
# Supported IR Protocols -Aiwa, BoseWave, Denon, Dish, JVC, Lego, LG, MagiQuest, NEC, Panasonic, RC5, RC6, Samsung, Sanyo, Sharp, Sony, Whynter, (Pronto).
-Protocols can be switched off and on by changing the lines in *IRremote.h*: +` NEC / Onkyo / Apple `     ` Denon / Sharp `     ` Panasonic / Kaseikyo ` + +` JVC `     ` LG `     ` RC5 `     ` RC6 `     ` Samsung `     ` Sony ` + +` Universal Pulse Distance `     ` Universal Pulse Width `     ` Universal Pulse Distance Width` + +` Hash `     ` Pronto ` + +` BoseWave `     ` Bang & Olufsen `     ` Lego `     ` FAST `     ` Whynter `     ` MagiQuest ` + +Protocols can be switched off and on by defining macros before the line `#include ` like [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiver/SimpleReceiver.ino#L33): + +```c++ +#define DECODE_NEC +//#define DECODE_DENON +#include +``` +
+ +# Common problem with IRremote +Or *"I build a gadged with 2 motors controlled by IR and the [IR stops after the first motor command](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#receiving-stops-after-analogwrite-or-tone-or-after-running-a-motor)"*.
+This is due to the fact, that the motor control by AnalogWrite() uses the same timer as IR receiving.
+See [this table](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage) for the list of timers and pins. + +# Using the new library version for old examples +This library has been refactored, breaking backward compatibility with the old version, on which many examples on the Internet are based. + +## New features of version 4.x +- **Since 4.3 `IrSender.begin(DISABLE_LED_FEEDBACK)` will no longer work**, use `IrSender.begin(DISABLE_LED_FEEDBACK, 0)` instead. +- New universal **Pulse Distance / Pulse Width / Pulse Distance Width decoder** added, which covers many previous unknown protocols. +- Printout of code how to send received command by `IrReceiver.printIRSendUsage(&Serial)`. +- RawData type is now 64 bit for 32 bit platforms and therefore `decodedIRData.decodedRawData` can contain complete frame information for more protocols than with 32 bit as before. +- **Callback** after receiving a command - It calls your code as soon as a message was received. +- Improved handling of `PULSE_DISTANCE` + `PULSE_WIDTH` protocols. +- New FAST protocol. +- Automatic printout of the **corresponding send function** with `printIRSendUsage()`. + +### Converting your 3.x program to the 4.x version +- You must replace `#define DECODE_DISTANCE` by `#define DECODE_DISTANCE_WIDTH` (only if you explicitly enabled this decoder). +- The parameter `bool hasStopBit` is not longer required and removed e.g. for function `sendPulseDistanceWidth()`. + +## New features of version 3.x +- **Any pin** can be used for receiving and if `SEND_PWM_BY_TIMER` is not defined also for sending. +- Feedback LED can be activated for sending / receiving. +- An 8/16 bit ****command** value as well as an 16 bit **address** and a protocol number is provided for decoding (instead of the old 32 bit value). +- Protocol values comply to **protocol standards**.
+ NEC, Panasonic, Sony, Samsung and JVC decode & send LSB first. +- Supports **Universal Distance protocol**, which covers a lot of previous unknown protocols. +- Compatible with **tone()** library. See the [ReceiveDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L284-L298) example. +- Simultaneous sending and receiving. See the [SendAndReceive](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SendAndReceive/SendAndReceive.ino#L167-L170) example. +- Supports **more platforms**. +- Allows for the generation of non PWM signal to just **simulate an active low receiver signal** for direct connect to existent receiving devices without using IR. +- Easy protocol configuration, **directly in your [source code](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiver/SimpleReceiver.ino#L33-L57)**.
+ Reduces memory footprint and decreases decoding time. +- Contains a [very small NEC only decoder](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#minimal-nec-receiver), which **does not require any timer resource**. + +[-> Feature comparison of 5 Arduino IR libraries](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#quick-comparison-of-5-arduino-ir-receiving-libraries). + +
+ +## Converting your 2.x program to the 4.x version +Starting with the 3.1 version, **the generation of PWM for sending is done by software**, thus saving the hardware timer and **enabling arbitrary output pins for sending**.
+If you use an (old) Arduino core that does not use the `-flto` flag for compile, you can activate the line `#define SUPPRESS_ERROR_MESSAGE_FOR_BEGIN` in IRRemote.h, if you get false error messages regarding begin() during compilation. + +- **IRreceiver** and **IRsender** object have been added and can be used without defining them, like the well known Arduino **Serial** object. +- Just remove the line `IRrecv IrReceiver(IR_RECEIVE_PIN);` and/or `IRsend IrSender;` in your program, and replace all occurrences of `IRrecv.` or `irrecv.` with `IrReceiver` and replace all `IRsend` or `irsend` with `IrSender`. +- Since the decoded values are now in `IrReceiver.decodedIRData` and not in `results` any more, remove the line `decode_results results` or similar. +- Like for the Serial object, call [`IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK)`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L106) + or `IrReceiver.begin(IR_RECEIVE_PIN, DISABLE_LED_FEEDBACK)` instead of the `IrReceiver.enableIRIn()` or `irrecv.enableIRIn()` in setup().
+For sending, call `IrSender.begin();` in setup().
+If IR_SEND_PIN is not defined (before the line `#include `) you must use e.g. `IrSender.begin(3, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN);` +- Old `decode(decode_results *aResults)` function is replaced by simple `decode()`. So if you have a statement `if(irrecv.decode(&results))` replace it with `if (IrReceiver.decode())`. +- The decoded result is now in in `IrReceiver.decodedIRData` and not in `results` any more, therefore replace any occurrences of `results.value` and `results.decode_type` (and similar) to + `IrReceiver.decodedIRData.decodedRawData` and `IrReceiver.decodedIRData.protocol`. +- Overflow, Repeat and other flags are now in [`IrReceiver.receivedIRData.flags`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRProtocol.h#L90-L101). +- Seldom used: `results.rawbuf` and `results.rawlen` must be replaced by `IrReceiver.decodedIRData.rawDataPtr->rawbuf` and `IrReceiver.decodedIRData.rawDataPtr->rawlen`. + +- The 5 protocols **NEC, Panasonic, Sony, Samsung and JVC** have been converted to LSB first. Send functions for sending old MSB data were renamed to `sendNECMSB`, `sendSamsungMSB()`, `sendSonyMSB()` and `sendJVCMSB()`. The old `sendSAMSUNG()` and `sendSony()` MSB functions are still available. The old MSB version of `sendPanasonic()` function was deleted, since it had bugs nobody recognized and therfore was assumed to be never used.
+For converting MSB codes to LSB see [below](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-to-convert-old-msb-first-32-bit-ir-data-codes-to-new-lsb-first-32-bit-ir-data-codes). + +### Example +#### Old 2.x program: + +```c++ +#include +#define RECV_PIN 2 + +IRrecv irrecv(RECV_PIN); +decode_results results; + +void setup() +{ +... + Serial.begin(115200); // Establish serial communication + irrecv.enableIRIn(); // Start the receiver +} + +void loop() { + if (irrecv.decode(&results)) { + Serial.println(results.value, HEX); + ... + irrecv.resume(); // Receive the next value + } + ... +} +``` + +#### New 4.x program: + +```c++ +#include +#define IR_RECEIVE_PIN 2 + +void setup() +{ +... + Serial.begin(115200); // // Establish serial communication + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); // Start the receiver +} + +void loop() { + if (IrReceiver.decode()) { + Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX); // Print "old" raw data + IrReceiver.printIRResultShort(&Serial); // Print complete received data in one line + IrReceiver.printIRSendUsage(&Serial); // Print the statement required to send this data + ... + IrReceiver.resume(); // Enable receiving of the next value + } + ... +} +``` + +#### Sample output +For more, see the [UnitTest log](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/UnitTest/UnitTest.log). + +``` +Protocol=NEC Address=0xF1 Command=0x76 Raw-Data=0x89760EF1 32 bits LSB first +Send with: IrSender.sendNEC(0xF1, 0x76, ); + +Protocol=Kaseikyo_Denon Address=0xFF1 Command=0x76 Raw-Data=0x9976FF10 48 bits LSB first +Send with: IrSender.sendKaseikyo_Denon(0xFF1, 0x76, ); +``` + +## How to convert old MSB first 32 bit IR data codes to new LSB first 32 bit IR data codes +For the new decoders for **NEC, Panasonic, Sony, Samsung and JVC**, the result `IrReceiver.decodedIRData.decodedRawData` is now **LSB-first**, as the definition of these protocols suggests!
+
+To convert one into the other, you must reverse the byte/nibble positions and then reverse all bit positions of each byte/nibble or write it as one binary string and reverse/mirror it.

+Example: +`0xCB 34 01 02`
+`0x20 10 43 BC` after nibble reverse
+`0x40 80 2C D3` after bit reverse of each nibble

+### Nibble reverse map: +``` + 0->0 1->8 2->4 3->C + 4->2 5->A 6->6 7->E + 8->1 9->9 A->5 B->D + C->3 D->B E->7 F->F +``` +`0xCB340102` is binary `1100 1011 0011 0100 0000 0001 0000 0010`.
+`0x40802CD3` is binary `0100 0000 1000 0000 0010 1100 1101 0011`.
+If you **read the first binary sequence backwards** (right to left), you get the second sequence. +You may use `bitreverseOneByte()` or `bitreverse32Bit()` for this. + +Sending old MSB codes without conversion can be done by using `sendNECMSB()`, `sendSonyMSB()`, `sendSamsungMSB()`, `sendJVCMSB()`. + +
+ +## Errors when using the 4.x versions for old tutorials +If you suffer from errors with old tutorial code including `IRremote.h` instead of `IRremote.hpp`, just try to rollback to [Version 2.4.0](https://github.com/Arduino-IRremote/Arduino-IRremote/releases/tag/v2.4.0).
+Most likely your code will run and you will not miss the new features. + +
+ +## Staying on 2.x +Consider using the [original 2.4 release form 2017](https://github.com/Arduino-IRremote/Arduino-IRremote/releases/tag/v2.4.0) +or the last backwards compatible [2.8 version](https://github.com/Arduino-IRremote/Arduino-IRremote/releases/tag/2.8.0) for you project.
+It may be sufficient and deals flawlessly with 32 bit IR codes.
+If this doesn't fit your case, be assured that 4.x is at least trying to be backwards compatible, so your old examples should still work fine. + +### Drawbacks of using 2.x +- Only the following decoders are available:
+ ` NEC `     ` Denon `     ` Panasonic `     ` JVC `     ` LG `
+ ` RC5 `     ` RC6 `     ` Samsung `     ` Sony ` +- The call of `irrecv.decode(&results)` uses the old MSB first decoders like in 2.x and sets the 32 bit codes in `results.value`. +- No decoding to a more meaningful (constant) 8/16 bit address and 8 bit command. + +
+ +# Why *.hpp instead of *.cpp? +**Every \*.cpp file is compiled separately** by a call of the compiler exclusively for this cpp file. These calls are managed by the IDE / make system. +In the Arduino IDE the calls are executed when you click on *Verify* or *Upload*. + +And now our problem with Arduino is:
+**How to set [compile options](#compile-options--macros-for-this-library) for all *.cpp files, especially for libraries used?**
+IDE's like [Sloeber](https://github.com/ArminJo/ServoEasing#modifying-compile-options--macros-with-sloeber-ide) or [PlatformIO](https://github.com/ArminJo/ServoEasing#modifying-compile-options--macros-with-platformio) support this by allowing to specify a set of options per project. +They add these options at each compiler call e.g. `-DTRACE`. + +But Arduino lacks this feature. +So the **workaround** is not to compile all sources separately, but to concatenate them to one huge source file by including them in your source.
+This is done by e.g. `#include "IRremote.hpp"`. + +But why not `#include "IRremote.cpp"`?
+Try it and you will see tons of errors, because each function of the *.cpp file is now compiled twice, +first by compiling the huge file and second by compiling the *.cpp file separately, like described above.
+So using the extension *cpp* is not longer possible, and one solution is to use *hpp* as extension, to show that it is an included *.cpp file.
+Every other extension e.g. *cinclude* would do, but *hpp* seems to be common sense. + +# Using the new *.hpp files +In order to support [compile options](#compile-options--macros-for-this-library) more easily, +you must use the statement `#include ` instead of `#include ` in your main program (aka *.ino file with setup() and loop()). + +In **all other files** you must use the following, to **prevent `multiple definitions` linker errors**: + +```c++ +#define USE_IRREMOTE_HPP_AS_PLAIN_INCLUDE +#include +``` + +**Ensure that all macros in your main program are defined before any** `#include `.
+The following macros will definitely be overridden with default values otherwise: +- `RAW_BUFFER_LENGTH` +- `IR_SEND_PIN` +- `SEND_PWM_BY_TIMER` + +
+ +# Tutorials +- A very elaborated introduction to IR remotes and IRremote library from [DroneBot Workshop ](https://dronebotworkshop.com/ir-remotes/). + + +# 3 ways to specify an IR code +There are 3 different ways of specifying a particular IR code. + +## 1. Timing +The timing of each mark/pulse and space/distance_between_pulses is specified in a list or array. +This enables specifying **all IR codes**, but requires a lot of memory and is **not readable at all**. +One formal definition of such a timing array, including **specification of frequency and repeats** is the [**Pronto** format](http://www.harctoolbox.org/Glossary.html#ProntoSemantics).
+Memory can be saved by using a lower time resolution. +For IRremote you can use a 50 µs resolution which halves the memory requirement by using byte values instead of int16 values. +For receiving purposes you can use the **hash of the timing** provided by the `decodeHash()` decoder. + +## 2. Encoding schemes +There are 3 main encoding schemes which encodes a binary bitstream / hex value: +1. `PULSE_DISTANCE`. The distance between pulses determines the bit value. This requires always a stop bit! +Examples are NEC and KASEIKYO protocols. The pulse width is constant for most protocols. +2. `PULSE_WIDTH`. The width of a pulse determines the bit value, pulse distance is constant. This requires no stop bit! +The only known example is the SONY protocol. +3. [Phase / Manchester encoding](https://en.wikipedia.org/wiki/Manchester_code). +The time of the pulse/pause transition (phase) relative to the clock determines the bit value. Examples are RC5 and RC6 protocols. + +Phase encoding has a **constant bit length**, `PULSE_DISTANCE` with constant pulse width and `PULSE_WIDTH` have **no constant bit length**! + +A well known example for `PULSE_DISTANCE` with non constant pulse width encoding is the **RS232 serial encoding**. +Here the non constant pulse width is used to enable a **constant bit length**. + +Most IR signals have a **special header** to help in setting the automatic gain of the receiver circuit. +This header is not part of the encoding, but is often significant for a special protocol and therefore must be reproducible. + +Be aware that there are codes using a `PULSE_DISTANCE` encoding where more than a binary 0/1 is put into a pulse/pause combination. +This requires more than 2 different pulse or pause length combinations. +The [HobToHood protocol](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveAndSendHob2Hood/ReceiveAndSendHob2Hood.ino) uses such an encoding. + +Using encoding schemes reduces the specification of an IR code to a bitstream / hex value, which is LSB by default and pulse / pause timings of header, 0, and 1. +The hex value is **quite readable**. +These schemes can not put any semantics like address, command or checksum on this bitstream. + +## 3. Protocols +There are a few common protocols that are implemented directly in IRremote. +They specify the frequency, the timings of header, 0, and 1 as well as other values like checksum, repeat distance, repeat coding, bit toggling etc. +The semantics of the hex value is also specified, allowing the usage of only 2 parameters **address** and **command** to specify an IR code. +This saves memory and is **highly readable**. +Often the address is also constant, which further reduces memory requirements. + + +# IRReceiver pinouts +![IRReceiver Pinout](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IRReceiverPinout.jpg) + +[Adafruit IR Sensor tutorial](https://learn.adafruit.com/ir-sensor) + + +# Receiving IR codes +In your program you check for a **completely received IR frame** with:
+`if (IrReceiver.decode()) {}`
+This also decodes the received data.
+After successful decoding, the IR data is contained in the IRData structure, available as `IrReceiver.decodedIRData`. + +## decodedIRData structure +```c++ +struct IRData { + decode_type_t protocol; // UNKNOWN, NEC, SONY, RC5, PULSE_DISTANCE, ... + uint16_t address; // Decoded address + uint16_t command; // Decoded command + uint16_t extra; // Used for Kaseikyo unknown vendor ID. Ticks used for decoding Distance protocol. + uint16_t numberOfBits; // Number of bits received for data (address + command + parity) - to determine protocol length if different length are possible. + uint8_t flags; // IRDATA_FLAGS_IS_REPEAT, IRDATA_FLAGS_WAS_OVERFLOW etc. See IRDATA_FLAGS_* definitions + IRRawDataType decodedRawData; // Up to 32 (64 bit for 32 bit CPU architectures) bit decoded raw data, used for sendRaw functions. + uint32_t decodedRawDataArray[RAW_DATA_ARRAY_SIZE]; // 32 bit decoded raw data, to be used for send function. + irparams_struct *rawDataPtr; // Pointer of the raw timing data to be decoded. Mainly the data buffer filled by receiving ISR. +}; +``` +#### Flags +This is the [list of flags](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRProtocol.h#L88) contained in the flags field.
+Check it with e.g. `if(IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT)`. + +| Flag name | Description | +|:---|----| +| IRDATA_FLAGS_IS_REPEAT | The gap between the preceding frame is as smaller than the maximum gap expected for a repeat. !!!We do not check for changed command or address, because it is almost not possible to press 2 different buttons on the remote within around 100 ms!!! +| IRDATA_FLAGS_IS_AUTO_REPEAT | The current repeat frame is a repeat, that is always sent after a regular frame and cannot be avoided. Only specified for protocols DENON, and LEGO. | +| IRDATA_FLAGS_PARITY_FAILED | The current (autorepeat) frame violated parity check. | +| IRDATA_FLAGS_TOGGLE_BIT | Is set if RC5 or RC6 toggle bit is set. | +| IRDATA_FLAGS_EXTRA_INFO | There is extra info not contained in address and data (e.g. Kaseikyo unknown vendor ID, or in decodedRawDataArray). | +| IRDATA_FLAGS_WAS_OVERFLOW | Too many marks and spaces for the specified `RAW_BUFFER_LENGTH`. To avoid endless flagging of overflow, irparams.rawlen is set to 0 in this case. | +| IRDATA_FLAGS_IS_MSB_FIRST | This value is mainly determined by the (known) protocol. | + +#### To access the **RAW data**, use: +```c++ +auto myRawdata= IrReceiver.decodedIRData.decodedRawData; +``` + +The definitions for the `IrReceiver.decodedIRData.flags` are described [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremoteInt.h#L128-L140). + +#### Print all fields: +```c++ +IrReceiver.printIRResultShort(&Serial); +``` + +#### Print the raw timing data received: +```c++ +IrReceiver.printIRResultRawFormatted(&Serial, true);` +``` +The raw data depends on the internal state of the Arduino timer in relation to the received signal and might therefore be slightly different each time. (resolution problem). The decoded values are the interpreted ones which are tolerant to such slight differences! + +#### Print how to send the received data: +```c++ +IrReceiver.printIRSendUsage(&Serial); +``` + +## Ambiguous protocols +### NEC, Extended NEC, ONKYO +The **NEC protocol** is defined as 8 bit address and 8 bit command. But the physical address and data fields are each 16 bit wide. +The additional 8 bits are used to send the inverted address or command for parity checking.
+The **extended NEC protocol** uses the additional 8 parity bit of address for a 16 bit address, thus disabling the parity check for address.
+The **ONKYO protocol** in turn uses the additional 8 parity bit of address and command for a 16 bit address and command. + +The decoder reduces the 16 bit values to 8 bit ones if the parity is correct. +If the parity is not correct, it assumes no parity error, but takes the values as 16 bit values without parity assuming extended NEC or extended NEC protocol protocol. + +But now we have a problem when we want to receive e.g. the **16 bit** address 0x00FF or 0x32CD! +The decoder interprets this as a NEC 8 bit address 0x00 / 0x32 with correct parity of 0xFF / 0xCD and reduces it to 0x00 / 0x32. + +One way to handle this, is to force the library to **always** use the ONKYO protocol interpretation by using `#define DECODE_ONKYO`. +Another way is to check if `IrReceiver.decodedIRData.protocol` is NEC and not ONKYO and to revert the parity reducing manually. + +### NEC, NEC2 +On a long press, the **NEC protocol** does not repeat its frame, it sends a special short repeat frame. +This enables an easy distinction between long presses and repeated presses and saves a bit of battery energy. +This behavior is quite unique for NEC and its derived protocols like LG and Samsung. + +But of course there are also remote control systems, that uses the NEC protocol but only repeat the first frame when a long press is made instead of sending the special short repeat frame. We named this the **NEC2** protocol and it is sent with `sendNEC2()`.
+But be careful, the NEC2 protocol can only be detected by the NEC library decoder **after** the first frame and if you do a long press! + +### Samsung, SamsungLG +On a long press, the **SamsungLG protocol** does not repeat its frame, it sends a special short repeat frame. + +## RAM usage of different protocols +The `RAW_BUFFER_LENGTH` determines the length of the **byte buffer** where the received IR timing data is stored before decoding.
+**100** is sufficient for standard protocols **up to 48 bits**, with 1 bit consisting of one mark and space. +We always require additional 4 bytes, 1 byte for initial gap, 2 bytes for header and 1 byte for stop bit. +- **48** bit protocols are PANASONIC, KASEIKYO, SAMSUNG48, RC6. +- **32** bit protocols like NEC, SAMSUNG, WHYNTER, SONY(20), LG(28) require a **buffer length of 68**. +- **16** bit protocols like BOSEWAVE, DENON, FAST, JVC, LEGO_PF, RC5, SONY(12 or 15) require a **buffer length of 36**. +- MAGIQUEST requires a buffer length of **112**. +- Air conditioners often send a longer protocol data stream **up to 750 bits**. + +If the record gap determined by `RECORD_GAP_MICROS` is changed from the default 8 ms to more than 20 ms, the buffer is no longer a byte but a uint16_t buffer, requiring twice as much RAM. +
+ +## Handling unknown Protocols +### Disclaimer +**This library was designed to fit inside MCUs with relatively low levels of resources and was intended to work as a library together with other applications which also require some resources of the MCU to operate.** + +Use the **ReceiveDemo example** to print out all informations about your IR protocol.
+The **ReceiveDump example** gives you more information but has bad repeat detection due to the time required for printing the information. + +### Other libraries, which may cover these protocols +#### IRMP +If your protocol seems not to be supported by this library, you may try the [IRMP library](https://github.com/IRMP-org/IRMP), which especially supports manchester protocols much better. + +#### IRremoteESP8266 +For **air conditioners** , you may try the [IRremoteESP8266 library](https://github.com/crankyoldgit/IRremoteESP8266), which supports an impressive set of protocols and a lot of air conditioners and works also on ESP32. + +#### rawirdecode and HeatpumpIR +[Raw-IR-decoder-for-Arduino](https://github.com/ToniA/Raw-IR-decoder-for-Arduino) is not a library, but an arduino example sketch, which provides many methods of decoding especially **air conditioner** protocols. Sending of these protocols can be done by the Arduino library [HeatpumpIR](https://github.com/ToniA/arduino-heatpumpir). + + +### Protocol=PULSE_DISTANCE +If you get something like this: +``` +PULSE_DISTANCE: HeaderMarkMicros=8900 HeaderSpaceMicros=4450 MarkMicros=550 OneSpaceMicros=1700 ZeroSpaceMicros=600 NumberOfBits=56 0x43D8613C 0x3BC3BC +``` +then you have a code consisting of **56 bits**, which is probably from an air conditioner remote.
+You can send it with `sendPulseDistanceWidth()`. +```c++ +uint32_t tRawData[] = { 0xB02002, 0xA010 }; +IrSender.sendPulseDistance(38, 3450, 1700, 450, 1250, 450, 400, &tRawData[0], 48, false, 0, 0); +``` +You can send it with calling `sendPulseDistanceWidthData()` twice, once for the first 32 bit and next for the remaining 24 bits.
+The `PULSE_DISTANCE` / `PULSE_WIDTH` decoder just decodes a timing stream to a bitstream stored as hex values. +These decoders can not put any semantics like address, command or checksum on this bitstream. +But the bitstream is way more readable, than a timing stream. This bitstream is read **LSB first by default**. +If LSB does not suit for further research, you can change it [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_DistanceProtocol.hpp#L78). + +**If RAM is not more than 2k, the decoder only accepts mark or space durations up to 2500 microseconds to save RAM space, otherwise it accepts durations up to 10 ms.** + +### Protocol=UNKNOWN +If you see something like `Protocol=UNKNOWN Hash=0x13BD886C 35 bits received` as output of e.g. the ReceiveDemo example, you either have a problem with decoding a protocol, or an unsupported protocol. + +- If you have an **odd number of bits** received, your receiver circuit probably has problems. Maybe because the IR signal is too weak. +- If you see timings like `+ 600,- 600 + 550,- 150 + 200,- 100 + 750,- 550` then one 450 µs space was split into two 150 and 100 µs spaces with a spike / error signal of 200 µs between. Maybe because of a defective receiver or a weak signal in conjunction with another light emitting source nearby. +- If you see timings like `+ 500,- 550 + 450,- 550 + 450,- 500 + 500,-1550`, then marks are generally shorter than spaces and therefore `MARK_EXCESS_MICROS` (specified in your ino file) should be **negative** to compensate for this at decoding. +- If you see `Protocol=UNKNOWN Hash=0x0 1 bits received` it may be that the space after the initial mark is longer than [`RECORD_GAP_MICROS`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremote.h#L124). + This was observed for some LG air conditioner protocols. Try again with a line e.g. `#define RECORD_GAP_MICROS 12000` before the line `#include ` in your .ino file. +- To see more info supporting you to find the reason for your UNKNOWN protocol, you must enable the line `//#define DEBUG` in IRremoteInt.h. + +### How to deal with protocols not supported by IRremote +If you do not know which protocol your IR transmitter uses, you have several choices. +- Just use the hash value to decide which command was received. See the [SimpleReceiverForHashCodes example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiverForHashCodes/SimpleReceiverForHashCodes.ino). +- Use the [IRreceiveDemo example](examples/ReceiveDemo) or [IRreceiveDump example](examples/ReceiveDump) to dump out the IR timing. + You can then reproduce/send this timing with the [SendRawDemo example](examples/SendRawDemo). +- The [IRMP AllProtocol example](https://github.com/IRMP-org/IRMP#allprotocol-example) prints the protocol and data for one of the **[40 supported protocols](https://github.com/IRMP-org/IRMP?tab=readme-ov-file#list-of-protocols)**. + The same library can be used to send this codes. +- If you have a bigger Arduino board at hand (> 100 kByte program memory) you can try the + [IRremoteDecode example](https://github.com/bengtmartensson/Arduino-DecodeIR/blob/master/examples/IRremoteDecode/IRremoteDecode.ino) of the Arduino library [DecodeIR](https://github.com/bengtmartensson/Arduino-DecodeIR). +- Use [IrScrutinizer](http://www.harctoolbox.org/IrScrutinizer.html). + It can automatically generate a send sketch for your protocol by exporting as "Arduino Raw". It supports IRremote, + the old [IRLib](https://github.com/cyborg5/IRLib) and [Infrared4Arduino](https://github.com/bengtmartensson/Infrared4Arduino). + +
+ +# Sending IR codes +If you have a device at hand which can generate the IR codes you want to work with (aka IR remote), +**it is recommended** to receive the codes with the [ReceiveDemo example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino), which will tell you on the serial output how to send them. + +``` +Protocol=LG Address=0x2 Command=0x3434 Raw-Data=0x23434E 28 bits MSB first +Send with: IrSender.sendLG(0x2, 0x3434, ); +``` +You will discover that **the address is a constant** and the commands sometimes are sensibly grouped.
+If you are uncertain about the numbers of repeats to use for sending, **3** is a good starting point. If this works, you can check lower values afterwards. +If you have enabled `DECODE_DISTANCE_WIDTH`, the code printed by `printIRSendUsage()` **differs between 8 and 32 bit platforms**, so it is best to run the receiving program on the same platform as the sending program. + +**All sending functions support the sending of repeats** if sensible. +Repeat frames are sent at a fixed period determined by the protocol. e.g. 110 ms from start to start for NEC.
+Keep in mind, that **there is no delay after the last sent mark**. +If you handle the sending of repeat frames by your own, you must insert sensible delays before the repeat frames to enable correct decoding. + +Sending old MSB codes without conversion can be done by using `sendNECMSB()`, `sendSonyMSB()`, `sendSamsungMSB()`, `sendJVCMSB()`. + +## Sending IRDB IR codes +The codes found in the [Flipper-IRDB database](https://github.com/Lucaslhm/Flipper-IRDB) are quite straightforward to convert, because the also use the address / command scheme.
+Protocol matching is NECext -> NECext (or Onkyo), Samsung32 -> Samsung, SIRC20 -> Sony with 20 bits etc. + +The codes found in the [irdb database](https://github.com/probonopd/irdb/tree/master/codes) specify a **device**, a **subdevice** and a **function**. +Most of the times, *device* and *subdevice* can be taken as upper and lower byte of the **address parameter** and *function* is the **command parameter** for the **new structured functions** with address, command and repeat-count parameters like e.g. `IrSender.sendNEC((device << 8) | subdevice, 0x19, 2)`.
+An **exact mapping** can be found in the [IRP definition files for IR protocols](https://github.com/probonopd/MakeHex/tree/master/protocols). "D" and "S" denotes device and subdevice and "F" denotes the function. + +## Send pin +Any pin can be chosen as send pin as long as `IR_SEND_PIN` is **not** defined. +This is because the PWM signal is generated by default with software bit banging, since `SEND_PWM_BY_TIMER` is not active.
+On **ESP32** ledc channel 0 is used for generating the IR PWM.
+If `IR_SEND_PIN` is specified (as C macro), it reduces program size and improves send timing for AVR. If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must disable this `IR_SEND_PIN` macro e.g. with `#undef IR_SEND_PIN`. +Then you can change send pin at any time before sending an IR frame. See also [Compile options / macros for this library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library). + +### List of public IR code databases +http://www.harctoolbox.org/IR-resources.html + +## Flipper Zero +[Flipper IRDB Database](https://github.com/Lucaslhm/Flipper-IRDB) + +| [Flipper decoding](https://github.com/flipperdevices/flipperzero-firmware/tree/release/lib/infrared/encoder_decoder) | [IRremote decoding](https://github.com/Arduino-IRremote/Arduino-IRremote/tree/master/src) | +|-|-| +| Samsung32 | Samsung | +| NEC | NEC | +| NECext | ONKYO | +| [\\\\\\\\\](https://github.com/flipperdevices/flipperzero-firmware/blob/027ea9ea36da137144548295c016d99255af53c3/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c#L26)
and ID is MSB of address.
address: 8A 02 20 00
command: 56 03 00 00
-> **IRremote:**
Address 0x6A8, sendPanasonic (for 02 20) and Command 0x35 | \\\\\\\ | + +
+ + +# Tiny NEC receiver and sender +For applications only requiring NEC, NEC variants or FAST -see below- protocol, there is a special receiver / sender included, +which has very **small code size of 500 bytes and does NOT require any timer**. + +## Principle of operation +Instead of sampling the input every 50 µs as IRremote does, TinyReceiver receiver uses a **pin change interrupt** for on-the-fly decoding which limits the choice of protocols.
+On each level change, the level and the time since the last change are used to incrementally decode the protocol.
+With this operating principle, we **cannot wait for a timeout** and then decode the protocol as IRremote does.
+Instead, we need to know which is the last bit (level change) of a protocol to do the final decoding +and the call of the optional **user provided callback function** `handleReceivedTinyIRData()`.
+This means, **we need to know the number of bits in a protocol** and therefore the protocol (family). + +Check out the [TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tinyreceiver--tinysender) and [IRDispatcherDemo](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#irdispatcherdemo) examples.
+Take care to include `TinyIRReceiver.hpp` or `TinyIRSender.hpp` instead of `IRremote.hpp`. + +### TinyIRReceiver usage +```c++ +//#define USE_ONKYO_PROTOCOL // Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value. +//#define USE_FAST_PROTOCOL // Use FAST protocol instead of NEC / ONKYO +#include "TinyIRReceiver.hpp" + +void setup() { + initPCIInterruptForTinyReceiver(); // Enables the interrupt generation on change of IR input signal +} + +void loop() { + if (TinyReceiverDecode()) { + printTinyReceiverResultMinimal(&Serial); + } + // No resume() required :-) +} ``` -#define DECODE_ 1 -#define SEND_ 1 + +### TinyIRSender usage +```c++ +#include "TinyIRSender.hpp" + +void setup() { + sendNEC(3, 0, 11, 2); // Send address 0 and command 11 on pin 3 with 2 repeats. +} + +void loop() {} +``` + +Another tiny receiver and sender **supporting more protocols** can be found [here](https://github.com/LuisMiCa/IRsmallDecoder). + +# The FAST protocol +The FAST protocol is a proprietary modified JVC protocol **without address, with parity and with a shorter header**. +It is meant to have a quick response to the event which sent the protocol frame on another board. +FAST takes **21 ms for sending** and is sent at a **50 ms period**. +It has full 8 bit parity for error detection. + +### FAST protocol characteristics: +- Bit timing is like JVC +- The header is shorter, 3156 µs vs. 12500 µs +- No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command, leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms. +- Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance. + +### Sending FAST protocol with IRremote +```c++ +#define IR_SEND_PIN 3 +#include + +void setup() { + sendFAST(11, 2); // Send command 11 on pin 3 with 2 repeats. +} + +void loop() {} ``` -# [Wiki](https://github.com/z3t0/Arduino-IRremote/wiki) -This is a quite old but maybe useful wiki for this library. + +### Sending FAST protocol with TinyIRSender +```c++ +#define USE_FAST_PROTOCOL // Use FAST protocol. No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command +#include "TinyIRSender.hpp" + +void setup() { + sendFAST(3, 11, 2); // Send command 11 on pin 3 with 2 repeats. +} + +void loop() {} +``` +
+ +The FAST protocol can be received by IRremote and TinyIRReceiver. + +# FAQ and hints +## Receiving stops after analogWrite() or tone() or after running a motor. +The receiver sample interval of 50 µs is generated by a timer. On many boards this must be a [hardware timer](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage). +On some boards where a software timer is available, the software timer is used.
+Be aware that the hardware timer used for receiving should not be used for `analogWrite()`.
+Especially **motor** control often uses the `analogWrite()` function and will therefore stop the receiving if used on the pins indicated [here](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage).
+On the Uno and other AVR boards the receiver timer ist the same as the tone timer. Thus receiving will stop after a `tone()` command. +See [ReceiveDemo example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L284-L298) how to deal with it, i.e. how to use `IrReceiver.restartTimer()`. + +## Receiving sets overflow flag. +The flag `IRDATA_FLAGS_WAS_OVERFLOW` is set, if `RAW_BUFFER_LENGTH` is too small for all the marks and spaces of the protocol. +This can happen on long protocol frames like the ones from air conditioner. +It also can happen, if `RECORD_GAP_MICROS` is smaller than the real gap between a frame and thr repetition frame, thus interpreting both as one consecutive frame. +Best is to dump the timing then, to see which reason holds. + +## Problems with Neopixels, FastLed etc. +IRremote will not work right when you use **Neopixels** (aka WS2811/WS2812/WS2812B) or other libraries blocking interrupts for a longer time (> 50 µs).
+Whether you use the Adafruit Neopixel lib, or FastLED, interrupts get disabled on many lower end CPUs like the basic Arduinos for longer than 50 µs. +In turn, this stops the IR interrupt handler from running when it needs to. See also this [video](https://www.youtube.com/watch?v=62-nEJtm070). + +One **workaround** is to wait for the IR receiver to be idle before you send the Neopixel data with `if (IrReceiver.isIdle()) { strip.show();}`.
+This **prevents at least breaking a running IR transmission** and -depending of the update rate of the Neopixel- may work quite well.
+There are some other solutions to this on more powerful processors, +[see this page from Marc MERLIN](http://marc.merlins.org/perso/arduino/post_2017-04-03_Arduino-328P-Uno-Teensy3_1-ESP8266-ESP32-IR-and-Neopixels.html) + +## Does not work/compile with another library +**Another library is only working/compiling** if you deactivate the line `IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);`.
+This is often due to **timer resource conflicts** with the other library. Please see [below](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage). + +## Multiple IR receiver and sender instances +**This library supports only one IR receiver and one IR sender object (IRrecv and IRsend) per CPU.**
+However since sending is a serial task, you can use `setSendPin()` to switch the pin to send, thus emulating multiple sender.
+The receiver uses a special **timer** triggered function, which reads the digital IR signal value from one pin every 50 µs.
+So **multiple IR receivers** can only be used by connecting the output pins of several IR receivers together. +The IR receiver modules internally use an NPN transistor as output device with just a 30k resistor to VCC. +This is basically an "open collector" and allows multiple output pins to be connected to one Arduino input pin.
+However, keep in mind that any weak / disturbed signal from one of the receivers will also interfere with a good signal from another receiver. + +## Increase strength of sent output signal +**The best way to increase the IR power for free** is to use 2 or 3 IR diodes in series. One diode requires 1.2 volt at 20 mA or 1.5 volt at 100 mA so you can supply up to 3 diodes with a 5 volt output.
+To power **2 diodes** with 1.2 V and 20 mA and a 5 V supply, set the resistor to: (5 V - 2.4 V) -> 2.6 V / 20 mA = **130 Ω**.
+For **3 diodes** it requires 1.4 V / 20 mA = **70 Ω**.
+The actual current might be lower since of **loss at the AVR pin**. E.g. 0.3 V at 20 mA.
+If you do not require more current than 20 mA, there is no need to use an external transistor (at least for AVR chips). + +On my Arduino Nanos, I always use a 100 Ω series resistor and one IR LED :grinning:. + +## Minimal CPU clock frequency +For receiving, the **minimal CPU clock frequency is 4 MHz**, since the 50 µs timer ISR (Interrupt Service Routine) takes around 12 µs on a 16 MHz ATmega.
+The TinyReceiver, which requires no polling, runs with 1 MHz.
+For sending, the **default software generated PWM has problems on AVR running with 8 MHz**. The PWM frequency is around 30 instead of 38 kHz and RC6 is not reliable. You can switch to timer PWM generation by `#define SEND_PWM_BY_TIMER`. + +## Bang & Olufsen protocol +The Bang & Olufsen protocol decoder is not enabled by default, i.e if no protocol is enabled explicitly by #define `DECODE_`. It must always be enabled explicitly by `#define DECODE_BEO`. +This is because it has an **IR transmit frequency of 455 kHz** and therefore requires a different receiver hardware (TSOP7000).
+And because **generating a 455 kHz PWM signal is currently only implemented for `SEND_PWM_BY_TIMER`**, sending only works if `SEND_PWM_BY_TIMER` or `USE_NO_SEND_PWM` is defined.
+For more info, see [ir_BangOlufsen.hpp](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_BangOlufsen.hpp#L44). + +# Examples for this library +The examples are available at File > Examples > Examples from Custom Libraries / IRremote.
+ In order to fit the examples to the 8K flash of ATtiny85 and ATtiny88, the [Arduino library ATtinySerialOut](https://github.com/ArminJo/ATtinySerialOut) is required for this CPU's.
+See also [DroneBot Workshop SimpleReceiver](https://dronebotworkshop.com/ir-remotes/#SimpleReceiver_Example_Code) and [SimpleSender](https://dronebotworkshop.com/ir-remotes/#SimpleSender_Example_Code). + +#### SimpleReceiver + SimpleSender +The **[SimpleReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiver/SimpleReceiver.ino)** and **[SimpleSender](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleSender/SimpleSender.ino)** examples are a good starting point. +A simple example can be tested online with [WOKWI](https://wokwi.com/projects/338611596994544210). + +#### SimpleReceiverForHashCodes +The **[SimpleReceiverForHashCodes](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiver/SimpleReceiver.ino)** uses only the hash decoder. +It converts all IR frames longer than 6 to a 32 bit hash code, thus enabling receiving of unknown protocols.
+See: http://www.righto.com/2010/01/using-arbitrary-remotes-with-arduino.html + +#### TinyReceiver + TinySender +If **code size** or **timer usage** matters, look at these examples.
+The **[TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/TinyReceiver/TinyReceiver.ino)** example uses the **TinyIRReceiver** library +which can **only receive NEC, Extended NEC, ONKYO and FAST protocols, but does not require any timer**. +They use pin change interrupt for on the fly decoding, which is the reason for the restricted protocol choice.
+TinyReceiver can be tested online with [WOKWI](https://wokwi.com/arduino/projects/339264565653013075). + +The **[TinySender](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/TinySender/TinySender.ino)** example uses the **TinyIRSender** library which can **only send NEC, ONKYO and FAST protocols**.
+It sends NEC protocol codes in standard format with 8 bit address and 8 bit command as in SimpleSender example. It has options to send using Extended NEC, ONKYO and FAST protocols. +Saves 780 bytes program memory and 26 bytes RAM compared to SimpleSender, which does the same, but uses the IRRemote library (and is therefore much more flexible). + +#### SmallReceiver +If the protocol is not NEC and code size matters, look at this [example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SmallReceiver/SmallReceiver.ino).
+ +#### ReceiveDemo + AllProtocolsOnLCD +[ReceiveDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino) receives all protocols and **generates a beep with the Arduino tone() function** on each packet received.
+Long press of one IR button (receiving of multiple repeats for one command) is detected.
+[AllProtocolsOnLCD](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/AllProtocolsOnLCD/AllProtocolsOnLCD.ino) additionally **displays the short result on a 1602 LCD**. The LCD can be connected parallel or serial (I2C).
+By connecting debug pin to ground, you can force printing of the raw values for each frame. The pin number of the debug pin is printed during setup, because it depends on board and LCD connection type.
+This example also serves as an **example how to use IRremote and tone() together**. + +#### ReceiveDump +Receives all protocols and dumps the received signal in different flavors including Pronto format. Since the printing takes much time, repeat signals may be skipped or interpreted as UNKNOWN. + +#### SendDemo +Sends all available protocols at least once. + +#### MultipleSendPins +Demonstrates sending IR codes toggling between 2 **different send pins**. + +#### SendAndReceive +Demonstrates **receiving while sending**. + +#### ReceiveAndSend +Record and **play back last received IR signal** at button press. IR frames of known protocols are sent by the appropriate protocol encoder. `UNKNOWN` protocol frames are stored as raw data and sent with `sendRaw()`. + +#### ReceiveAndSendDistanceWidth +Try to decode each IR frame with the *universal* **DistanceWidth decoder**, store the data and send it on button press with `sendPulseDistanceWidthFromArray()`.
+If RAM is not more than 2k, the decoder only accepts mark or space durations up to 2500 microseconds to save RAM space, otherwise it accepts durations up to 10 ms.
+Storing data for distance width protocol requires 17 bytes. +The ReceiveAndSend example requires 16 bytes for known protocol data and 37 bytes for raw data of e.g.NEC protocol. + +#### ReceiveOneAndSendMultiple +Serves as a IR **remote macro expander**. Receives Samsung32 protocol and on receiving a specified input frame, it sends multiple Samsung32 frames with appropriate delays in between. +This serves as a **Netflix-key emulation** for my old Samsung H5273 TV. + +#### IRDispatcherDemo +Framework for **calling different functions of your program** for different IR codes. + +#### IRrelay +**Control a relay** (connected to an output pin) with your remote. + +#### IRremoteExtensionTest +[Example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/IRremoteExtensionTest/IRremoteExtensionTest.ino) for a user defined class, which itself uses the IRrecv class from IRremote. + +#### SendLGAirConditionerDemo +[Example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SendLGAirConditionerDemo/SendLGAirConditionerDemo.ino) for sending LG air conditioner IR codes controlled by Serial input.
+By just using the function `bool Aircondition_LG::sendCommandAndParameter(char aCommand, int aParameter)` you can control the air conditioner by any other command source.
+The file *acLG.h* contains the command documentation of the LG air conditioner IR protocol. Based on reverse engineering of the LG AKB73315611 remote. +![LG AKB73315611 remote](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/LG_AKB73315611.jpg)
+IReceiverTimingAnalysis can be tested online with [WOKWI](https://wokwi.com/projects/299033930562011656) +Click on the receiver while simulation is running to specify individual IR codes. + +#### ReceiveAndSendHob2Hood +[Example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SendLGAirConditionerDemo/ReceiveAndSendHobToHood.ino) for receiving and sending AEG / Elektrolux Hob2Hood protocol.
+ +#### ReceiverTimingAnalysis +This [example](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiverTimingAnalysis/ReceiverTimingAnalysis.ino) analyzes the signal delivered by your IR receiver module. +Values can be used to determine the stability of the received signal as well as a hint for determining the protocol.
+It also computes the `MARK_EXCESS_MICROS` value, which is the extension of the mark (pulse) duration introduced by the IR receiver module.
+It can be tested online with [WOKWI](https://wokwi.com/arduino/projects/299033930562011656). +Click on the receiver while simulation is running to specify individual NEC IR codes. + +#### UnitTest +ReceiveDemo + SendDemo in one program. Demonstrates **receiving while sending**. +Here you see the delay of the receiver output (blue) from the IR diode input (yellow). +![Delay](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_UnitTest_delay.bmp) + +# WOKWI online examples +- [Simple receiver](https://wokwi.com/projects/338611596994544210) +- [Simple toggle by IR key 5](https://wokwi.com/projects/338611596994544210) +- [TinyReceiver](https://wokwi.com/arduino/projects/339264565653013075) +- [ReceiverTimingAnalysis](https://wokwi.com/projects/299033930562011656) +- [Receiver with LCD output and switch statement](https://wokwi.com/projects/298934082074575369) + +# IR control of a robot car +This [example](https://github.com/ArminJo/PWMMotorControl?tab=readme-ov-file#basicircontrol) of the **Arduino PWMMotorControl library** controls the basic functions of a robot car using the IRremote library.
+It controls 2 PWM motor channels, 2 motors at each channel.
+[Here](https://www.instructables.com/Arduino-4WD-Car-Assembly-and-Code-With-Optional-In/) you can find the instructable for car assembly and code.
+ +IR_RobotCar with TL1838 IR receiver plugged into expansion board.
+![IR_RobotCar](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_RobotCar.jpg) + +
+ +# Issues and discussions +- Do not open an issue without first testing some of the examples! +- If you have a problem, please post the MCVE (Minimal Complete Verifiable Example) showing this problem. My experience is, that most of the times you will find the problem while creating this MCVE :smile:. +- [Use code blocks](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code); **it helps us to help you when we can read your code!** + +
# Compile options / macros for this library -To customize the library to different requirements, there are some compile options / makros available.
-Modify it by commenting them out or in, or change the values if applicable. Or define the macro with the -D compiler option for gobal compile (the latter is not possible with the Arduino IDE, so consider to use [Sloeber](https://eclipse.baeyens.it). +To customize the library to different requirements, there are some compile options / macros available.
+These macros must be defined in your program **before** the line `#include ` to take effect.
+Modify them by enabling / disabling them, or change the values if applicable. -| Name | File | Default value | Description | -|-|-|-|-| -| `DEBUG` | IRremote.h | disabled | Enables lots of lovely debug output. | -| `USE_NEC_STANDARD` | IRremote.h | disabled | Use LSB first, address/code schema for encoding. | -| `USE_NO_SEND_PWM` | IRremote.h | disabled | Use no carrier PWM, just simulate an active low receiver signal. | -| `USE_SOFT_SEND_PWM` | IRremote.h | disabled | Use carrier PWM generation in software, instead of hardware PWM. | -| `PULSE_CORRECTION_MICROS` | IRremote.h | 3 | If USE_SOFT_SEND_PWM, this amount is subtracted from the on-time of the pulses. | -| `USE_SPIN_WAIT` | IRremote.h | disabled | If USE_SOFT_SEND_PWM, use spin wait instead of delayMicros(). | -| `RAW_BUFFER_LENGTH` | IRremoteint.h | 101 | Buffer size of raw input buffer. Must be odd! | -| `IR_SEND_DUTY_CYCLE` | IRremoteBoardDefs.h | 30 | Duty cycle of IR send signal. | -| `MICROS_PER_TICK` | IRremoteBoardDefs.h | 50 | Resolution of the raw input buffer data. | - -### Modifying library properties with Arduino IDE -First use *Sketch/Show Sketch Folder (Ctrl+K)*.
-If you did not yet stored the example as your own sketch, then you are instantly in the right library folder.
+| Name | Default value | Description | +|-|-:|-| +| `RAW_BUFFER_LENGTH` | 200 | Buffer size of raw input uint16_t buffer. Must be even! If it is too small, overflow flag will be set. 100 is sufficient for *regular* protocols of up to 48 bits, but for most air conditioner protocols a value of up to 750 is required. Use the ReceiveDump example to find smallest value for your requirements. A value of 200 requires 200 bytes RAM. | +| `EXCLUDE_UNIVERSAL_PROTOCOLS` | disabled | Excludes the universal decoder for pulse distance width protocols and decodeHash (special decoder for all protocols) from `decode()`. Saves up to 1000 bytes program memory. | +| `EXCLUDE_EXOTIC_PROTOCOLS` | disabled | Excludes BANG_OLUFSEN, BOSEWAVE, WHYNTER, FAST and LEGO_PF from `decode()` and from sending with `IrSender.write()`. Saves up to 650 bytes program memory. | +| `DECODE_` | all | Selection of individual protocol(s) to be decoded. You can specify multiple protocols. See [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremote.hpp#L98-L121) | +| `DECODE_STRICT_CHECKS` | disabled | Check for additional required characteristics of protocol timing like length of mark for a constant mark protocol, where space length determines the bit value. Requires up to 194 additional bytes of program memory. | +| `IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK` | disabled | Saves up to 60 bytes of program memory and 2 bytes RAM. | +| `MARK_EXCESS_MICROS` | 20 | MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding, to compensate for the signal forming of different IR receiver modules. | +| `RECORD_GAP_MICROS` | 5000 | Minimum gap between IR transmissions, to detect the end of a protocol.
Must be greater than any space of a protocol e.g. the NEC header space of 4500 µs.
Must be smaller than any gap between a command and a repeat; e.g. the retransmission gap for Sony is around 24 ms.
Keep in mind, that this is the delay between the end of the received command and the start of decoding. | +| `DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE` | 50 if RAM <= 2k, else 200 | A value of 200 allows to decode mark or space durations up to 10 ms. | +| `IR_INPUT_IS_ACTIVE_HIGH` | disabled | Enable it if you use a RF receiver, which has an active HIGH output signal. | +| `IR_SEND_PIN` | disabled | If specified, it reduces program size and improves send timing for AVR. If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must not use / disable this macro in your source. | +| `SEND_PWM_BY_TIMER` | disabled | Disables carrier PWM generation in software and use hardware PWM (by timer). Has the advantage of more exact PWM generation, especially the duty cycle (which is not very relevant for most IR receiver circuits), and the disadvantage of using a hardware timer, which in turn is not available for other libraries and to fix the send pin (but not the receive pin) at the [dedicated timer output pin(s)](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage). Is enabled for ESP32 and RP2040 in all examples, since they support PWM gereration for each pin without using a shared resource (timer). | +| `IR_SEND_DUTY_CYCLE_PERCENT` | 30 | Duty cycle of IR send signal. | +| `USE_NO_SEND_PWM` | disabled | Uses no carrier PWM, just simulate an **active low** receiver signal. Used for transferring signal by cable instead of IR. Overrides `SEND_PWM_BY_TIMER` definition. | +| `USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN` | disabled | Uses or simulates open drain output mode at send pin. **Attention, active state of open drain is LOW**, so connect the send LED between positive supply and send pin! | +| `USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN` | disabled | Only if `USE_NO_SEND_PWM` is enabled. Simulate an **active high** receiver signal instead of an active low signal. | +| `DISABLE_CODE_FOR_RECEIVER` | disabled | Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. | +| `FEEDBACK_LED_IS_ACTIVE_LOW` | disabled | Required on some boards (like my BluePill and my ESP8266 board), where the feedback LED is active low. | +| `NO_LED_FEEDBACK_CODE` | disabled | Disables the LED feedback code for send and receive. Saves around 100 bytes program memory for receiving, around 500 bytes for sending and halving the receiver ISR (Interrupt Service Routine) processing time. | +| `NO_LED_RECEIVE_FEEDBACK_CODE` | disabled | Disables the LED feedback code for receive. Saves around 100 bytes program memory for receiving and halving the receiver ISR (Interrupt Service Routine) processing time. | +| `NO_LED_SEND_FEEDBACK_CODE` | disabled | Disables the LED feedback code for send. Saves around 500 bytes for sending. | +| `MICROS_PER_TICK` | 50 | Resolution of the raw input buffer data. Corresponds to 2 pulses of each 26.3 µs at 38 kHz. | +| `TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT` | 25 | Relative tolerance for matchTicks(), matchMark() and matchSpace() functions used for protocol decoding. | +| `DEBUG` | disabled | Enables lots of lovely debug output. | +| `IR_USE_AVR_TIMER*` | | Selection of timer to be used for generating IR receiving sample interval. | + +These next macros for **TinyIRReceiver** must be defined in your program before the line `#include ` to take effect. +| Name | Default value | Description | +|-|-:|-| +| `IR_RECEIVE_PIN` | 2 | The pin number for TinyIRReceiver IR input, which gets compiled in. Not used in IRremote. | +| `IR_FEEDBACK_LED_PIN` | `LED_BUILTIN` | The pin number for TinyIRReceiver feedback LED, which gets compiled in. | +| `NO_LED_FEEDBACK_CODE` | disabled | Disables the feedback LED codefor send and receive. Saves 14 bytes program memory. | +| `NO_LED_RECEIVE_FEEDBACK_CODE` | disabled | Disables the LED feedback code for receive. | +| `NO_LED_SEND_FEEDBACK_CODE` | disabled | Disables the LED feedback code for send. | +| `DISABLE_PARITY_CHECKS` | disabled | Disables the addres and command parity checks. Saves 48 bytes program memory. | +| `USE_EXTENDED_NEC_PROTOCOL` | disabled | Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value. | +| `USE_ONKYO_PROTOCOL` | disabled | Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value. | +| `USE_FAST_PROTOCOL` | disabled | Use FAST protocol (no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command) instead of NEC. | +| `ENABLE_NEC2_REPEATS` | disabled | Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat. | +| `USE_CALLBACK_FOR_TINY_RECEIVER` | disabled | Call the user provided function `void handleReceivedTinyIRData()` each time a frame or repeat is received. | + +The next macro for **IRCommandDispatcher** must be defined in your program before the line `#include ` to take effect. +| `USE_TINY_IR_RECEIVER` | disabled | Use [TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tinyreceiver--tinysender) for receiving IR codes. | +| `IR_COMMAND_HAS_MORE_THAN_8_BIT` | disabled | Enables mapping and dispatching of IR commands consisting of more than 8 bits. Saves up to 160 bytes program memory and 4 bytes RAM + 1 byte RAM per mapping entry. | +| `BUZZER_PIN` | | If `USE_TINY_IR_RECEIVER` is enabled, the pin to be used for the optional 50 ms buzzer feedback before executing a command. Other IR libraries than Tiny are not compatible with tone() command. | + +### Changing include (*.h) files with Arduino IDE +First, use *Sketch > Show Sketch Folder (Ctrl+K)*.
+If you have not yet saved the example as your own sketch, then you are instantly in the right library folder.
Otherwise you have to navigate to the parallel `libraries` folder and select the library you want to access.
-In both cases the library files itself are located in the `src` directory.
+In both cases the library source and include files are located in the libraries `src` directory.
+The modification must be renewed for each new library version! -### Modifying library properties with Sloeber IDE -If you are using Sloeber as your IDE, you can easily define global symbols with *Properties/Arduino/CompileOptions*.
-![Sloeber settings](https://github.com/ArminJo/ServoEasing/blob/master/pictures/SloeberDefineSymbols.png) +### Modifying compile options / macros with PlatformIO +If you are using PlatformIO, you can define the macros in the *[platformio.ini](https://docs.platformio.org/en/latest/projectconf/section_env_build.html)* file with `build_flags = -D MACRO_NAME` or `build_flags = -D MACRO_NAME=macroValue`. -# Handling unknown Protocols -## Disclaimer -This library was never designed to handle long codes like the ones used by air conditioners. See [Recording long Infrared Remote control signals with Arduino](https://www.analysir.com/blog/2014/03/19/air-conditioners-problems-recording-long-infrared-remote-control-signals-arduino).
-The main reason is, that it was designed to fit inside MCUs with relatively low levels of resources and was intended to work as a library together with other applications which also require some resources of the MCU to operate. +### Modifying compile options / macros with Sloeber IDE +If you are using [Sloeber](https://eclipse.baeyens.it) as your IDE, you can easily define global symbols with *Properties > Arduino > CompileOptions*.
+![Sloeber settings](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/SloeberDefineSymbols.png) -## Hints -If you do not know which protocol your IR transmitter uses, you have several choices. -- Use the [IRreceiveDumpV2 example](examples/IRreceiveDumpV2) to dump out the IR timing. You can then reproduce/send this timing with the [IRsendRawDemo example](examples/IRsendRawDemo). For **long codes** like from air conditioners, you can **change the length of the input buffer** in [IRremoteInt.h](src/private/IRremoteInt.h#L30). -- If you have a bigger Arduino board at hand (> 100 kByte program space) you can try the [IRremoteDecode example](https://github.com/bengtmartensson/Arduino-DecodeIR/blob/master/examples/IRremoteDecode/IRremoteDecode.ino) of the Arduino library [DecodeIR](https://github.com/bengtmartensson/Arduino-DecodeIR). -- Use [IrScrutinizer](http://www.harctoolbox.org/IrScrutinizer.html). It can automatically generate a send sketch for your protocol by exporting as "Arduino Raw". It supports IRremote, the old [IRLib](https://github.com/cyborg5/IRLib) and [Infrared4Arduino](https://github.com/bengtmartensson/Infrared4Arduino). -- Use the [IRMP AllProtocol example](https://github.com/ukw100/IRMP#allprotocol-example) and you will see some serial output if the protocol is one of the 40 supported protocols. -- To increase strength of sent output signal you can increase the current through the send diode, or use 2 diodes in series, since one IR diode requires only 1.5 volt. Changing `IR_SEND_DUTY_CYCLE` to 50 increases the signal current by 40%. - -## Other IR libraries -[Here](https://github.com/ukw100/IRMP#quick-comparison-of-4-arduino-ir-receiving-libraries) you find a **short comparison matrix** of 4 popular Arduino IR libraries.
-[Here](https://github.com/crankyoldgit/IRremoteESP8266) you find an **ESP8266/ESP32** version of IRremote with an **[impressive list of supported protocols](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/SupportedProtocols.md)**. +
# Supported Boards +**Issues and discussions with the content "Is it possible to use this library with the ATTinyXYZ? / board XYZ" without any reasonable explanations will be immediately closed without further notice.**
+For **ESP8266/ESP32**, [this library](https://github.com/crankyoldgit/IRremoteESP8266) supports an [impressive set of protocols and a lot of air conditioners](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/SupportedProtocols.md)
+
+Digispark boards are only tested with [ATTinyCore](https://github.com/SpenceKonde/ATTinyCore) using `New Style` pin mapping for the Digispark Pro board.
+ATtiny boards are only tested with [ATTinyCore](https://github.com/SpenceKonde/ATTinyCore#supported-devices) or [megaTinyCore](https://github.com/SpenceKonde/megaTinyCore). + - Arduino Uno / Mega / Leonardo / Duemilanove / Diecimila / LilyPad / Mini / Fio / Nano etc. -- Teensy 1.0 / 1.0++ / 2.0 / 2++ / 3.0 / 3.1 / Teensy-LC; Credits: @PaulStoffregen (Teensy Team) +- Arduino Uno R4, but not yet tested, because of lack of a R4 board. **Sending does not work** on the `arduino:renesas_uno:unor4wifi`. +- Teensy 1.0 / 1.0++ / 2.0 / 2++ / 3.0 / 3.1 / 3.2 / Teensy-LC - but [limited support](https://forum.pjrc.com/threads/65912-Enable-Continuous-Integration-with-arduino-cli-for-3-party-libraries); Credits: PaulStoffregen (Teensy Team) - Sanguino - ATmega8, 48, 88, 168, 328 - ATmega8535, 16, 32, 164, 324, 644, 1284, - ATmega64, 128 - ATmega4809 (Nano every) -- ATtiny84, 85 -- SAMD21 (receive only) -- ESP32 -- ESP8266 is supported in a fork based on an old codebase that isn't as recent, but it works reasonably well given that perfectly timed sub millisecond interrupts are different on that chip. See https://github.com/markszabo/IRremoteESP8266 +- ATtiny3217 (Tiny Core 32 Dev Board) +- ATtiny84, 85, 167 (Digispark + Digispark Pro) +- SAMD (Zero, MKR*, **but not DUE, the latter is SAM architecture**) +- ESP8266 +- ESP32 (ESP32-C3 since board package 2.0.2 from Espressif) - Sparkfun Pro Micro +- Nano Every, Uno WiFi Rev2, nRF5 BBC MicroBit, Nano33_BLE +- BluePill with STM32 +- RP2040 based boards (Raspberry Pi Pico, Nano RP2040 Connect etc.) + + + +We are open to suggestions for adding support to new boards, however we highly recommend you contact your supplier first and ask them to provide support from their side.
+If you can provide **examples of using a periodic timer for interrupts** for the new board, and the board name for selection in the Arduino IDE, then you have way better chances to get your board supported by IRremote. + +
+ +# Timer and pin usage +The **receiver sample interval of 50 µs is generated by a timer**. On many boards this must be a hardware timer. On some boards where a software timer is available, the software timer is used.
+On **ESP8266** `timer1` is used for receive interrupts, which makes it incompatible to the Servo and other libraries.
+On **ESP32** `hw_timer_t` is used for receive interrupts. + +Every pin can be used for receiving.
+If software PWM is selected, which is default, every pin can also be used for sending. Sending with software PWM does not require a timer! + +The TinyReceiver example uses the **TinyReceiver** library, which can **only receive NEC codes, but does not require any timer** and runs even on a 1 MHz ATtiny85. + +The code for the timer and the **timer selection** is located in [private/IRTimer.hpp](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/private/IRTimer.hpp). The selected timer can be adjusted here. + +**Be aware that the hardware timer used for receiving should not be used for analogWrite()!**.
+ +| Board/CPU | Receive
& send PWM Timer
Default timer is **bold** | Hardware-Send-PWM Pin | analogWrite()
pins occupied by timer | +|-|-|-|-| +| [ATtiny84](https://github.com/SpenceKonde/ATTinyCore/blob/v2.0.0-devThis-is-the-head-submit-PRs-against-this/avr/extras/ATtiny_x4.md) | **1** | **6** | | +| [ATtiny85 > 4 MHz](https://github.com/SpenceKonde/ATTinyCore/blob/v2.0.0-devThis-is-the-head-submit-PRs-against-this/avr/extras/ATtiny_x5.md) | **0**, 1 | **0**, 4 | **0**, 1 & 4 | +| [ATtiny88 > 4 MHz](https://github.com/SpenceKonde/ATTinyCore/blob/v2.0.0-devThis-is-the-head-submit-PRs-against-this/avr/extras/ATtiny_x8.md) | **1** | **PB1 / 8** | **PB1 / 8 & PB2 / 9** | +| [ATtiny167 > 4 MHz](https://github.com/SpenceKonde/ATTinyCore/blob/v2.0.0-devThis-is-the-head-submit-PRs-against-this/avr/extras/ATtiny_x7.md) | **1** | **9**, 8 - 15 | **8 - 15** | +| [ATtiny1604](https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/ATtiny_x04.md) | **TCB0** | **PA05** | +| [ATtiny1614, ATtiny816](https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/ATtiny_x14.md) | **TCA0** | **PA3** | +| [ATtiny3217](https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/ATtiny_x17.md) | **TCA0**, TCD | % | +| [ATmega8](https://github.com/MCUdude/MiniCore#supported-microcontrollers) | **1** | **9** | +| [ATmega1284](https://github.com/MCUdude/MightyCore#supported-microcontrollers) | 1, **2**, 3 | 13, 14, 6 | +| [ATmega164, ATmega324, ATmega644](https://github.com/MCUdude/MightyCore#supported-microcontrollers) | 1, **2** | 13, **14** | +| [ATmega8535 ATmega16, ATmega32](https://github.com/MCUdude/MightyCore#supported-microcontrollers) | **1** | **13** | +| [ATmega64, ATmega128, ATmega1281, ATmega2561](https://github.com/MCUdude/MegaCore#supported-microcontrollers) | **1** | **13** | +| [ATmega8515, ATmega162](https://github.com/MCUdude/MajorCore#pinout ) | **1** | **13** | +| ATmega168, **ATmega328** | 1, **2** | 9, **3** | 9 & 10, **3 & 11** | +| ATmega1280, **ATmega2560** | 1, **2**, 3, 4, 5 | 5, 6, **9**, 11, 46 | 5, 6, **9**, 11, 46 | +| ATmega4809 | **TCB0** | **A4** | | +| Leonardo (Atmega32u4) | 1, 3, **4_HS** | 5, **9**, 13 | 5, **9**, 13 | +| Zero (SAMD) | **TC3** | \*, **9** | | +| [ESP8266](http://esp8266.net/) | **timer1** | % | | +| [ESP32](http://esp32.net/) | **hw_timer_t**
**Ledc channel 0** | All pins | | +| [Sparkfun Pro Micro](https://www.sparkfun.com/products/12640) | 1, **3** | **5**, 9 | | +| [Teensy 1.0](https://www.pjrc.com/teensy/pinout.html) | **1** | **17** | 15, 18 | +| [Teensy 2.0](https://www.pjrc.com/teensy/pinout.html) | 1, 3, **4_HS** | 9, **10**, 14 | 12 | +| [Teensy++ 1.0 / 2.0](https://www.pjrc.com/teensy/pinout.html) | 1, **2**, 3 | **1**, 16, 25 | 0 | +| [Teensy-LC](https://www.pjrc.com/teensy/pinout.html) | **TPM1** | **16** | 17 | +| [Teensy 3.0 - 3.6](https://www.pjrc.com/teensy/pinout.html) | **CMT** | **5** | | +| [Teensy 4.0 - 4.1](https://www.pjrc.com/teensy/pinout.html) | **FlexPWM1.3** | **8** | 7, 25 | +| [BluePill / STM32F103C8T6](https://github.com/stm32duino/Arduino_Core_STM32) | **3** | % | **PA6 & PA7 & PB0 & PB1** | +| [BluePill / STM32F103C8T6](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill) | **TIM4** | % | **PB6 & PB7 & PB8 & PB9** | +| [RP2040 / Pi Pico](https://github.com/earlephilhower/arduino-pico) | [default alarm pool](https://raspberrypi.github.io/pico-sdk-doxygen/group__repeating__timer.html) | All pins | No pin | +| [RP2040 / Mbed based](https://github.com/arduino/ArduinoCore-mbed) | Mbed Ticker | All pins | No pin | + +### No timer required for sending +The **send PWM signal** is by default generated by software. **Therefore every pin can be used for sending**. +The PWM pulse length is guaranteed to be constant by using `delayMicroseconds()`. +Take care not to generate interrupts during sending with software generated PWM, otherwise you will get jitter in the generated PWM. +E.g. wait for a former `Serial.print()` statement to be finished by `Serial.flush()`. +Since the Arduino `micros()` function has a resolution of 4 µs at 16 MHz, we always see a small jitter in the signal, which seems to be OK for the receivers. + +| Software generated PWM showing small jitter because of the limited resolution of 4 µs of the Arduino core `micros()` function for an ATmega328 | Detail (ATmega328 generated) showing 30% duty cycle | +|-|-| +| ![Software PWM](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_PWM_by_software_jitter.png) | ![Software PWM detail](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_PWM_by_software_detail.png) | + +## Incompatibilities to other libraries and Arduino commands like tone() and analogWrite() +If you use a library which requires the same timer as IRremote, you have a problem, since **the timer resource cannot be shared simultaneously** by both libraries. + +### Use NEC protocol and TinyReceiver +[TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tiny-nec-receiver-and-sender) does not require a timer, it relies on interrupts, thus avoiding any timer resource problems. + +### Change timer +The best approach is to **change the timer** used for IRremote, which can be accomplished by specifying the timer before `#include `.
+The timer specifications available for your board can be found in [private/IRTimer.hpp](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/private/IRTimer.hpp).
+ +```c++ +// Arduino Mega +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +# if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER2) && !defined(IR_USE_AVR_TIMER3) && !defined(IR_USE_AVR_TIMER4) && !defined(IR_USE_AVR_TIMER5) +//#define IR_USE_AVR_TIMER1 // send pin = pin 11 +#define IR_USE_AVR_TIMER2 // send pin = pin 9 +//#define IR_USE_AVR_TIMER3 // send pin = pin 5 +//#define IR_USE_AVR_TIMER4 // send pin = pin 6 +//#define IR_USE_AVR_TIMER5 // send pin = pin 46 +# endif +``` +Here you see the Arduino Mega board and the available specifications are `IR_USE_AVR_TIMER[1,2,3,4,5]`.
+You **just have to include a line** e.g. `#define IR_USE_AVR_TIMER3` before `#include ` to enable timer 3. + +But be aware that the new timer in turn might be again incompatible with other libraries or Arduino functions.
+For non AVR boards/platforms you must look for the appropriate section guarded by e.g. `#elif defined(ESP32)`. + +### Stop and start timer +Another approach can be to share the timer **sequentially** if their functionality is used only for a short period of time like for the **Arduino tone() command**. +An example can be seen [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L284-L298), where the IR timer is restarted after the tone has stopped. + +```c++ +IrReceiver.stopTimer(); // Stop timer consistently before calling tone() or other functions using the timer resource. +tone(TONE_PIN, 2200, 8); +delay(8); +IrReceiver.restartTimer(); // Restart IR timer after timer resource is no longer blocked. +``` +This works on AVR boards like Uno because each call to` tone()` completely initializes the timer 2 used by the `tone()` command. + +## Hardware-PWM signal generation for sending +If you define `SEND_PWM_BY_TIMER`, the send PWM signal is forced to be generated by a hardware timer on most platforms.
+By default, the same timer as for the receiver is used.
+Since each hardware timer has its dedicated output pin(s), you must change timer or timer sub-specifications to change PWM output pin. See [private/IRTimer.hpp](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/private/IRTimer.hpp)
+**Exeptions** are currently [ESP32, ARDUINO_ARCH_RP2040, PARTICLE and ARDUINO_ARCH_MBED](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleSender/PinDefinitionsAndMore.h#L334), where **PWM generation does not require a timer**. + +## Why do we use 30% duty cycle for sending +We [do it](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRSend.hpp#L1192) according to the statement in the [Vishay datasheet](https://www.vishay.com/docs/80069/circuit.pdf): +- Carrier duty cycle 50 %, peak current of emitter IF = 200 mA, the resulting transmission distance is 25 m. +- Carrier duty cycle 10 %, peak current of emitter IF = 800 mA, the resulting transmission distance is 29 m. - Factor 1.16 +The reason is, that it is not the pure energy of the fundamental which is responsible for the receiver to detect a signal. +Due to automatic gain control and other bias effects, high intensity of the 38 kHz pulse counts more than medium intensity (e.g. 50% duty cycle) at the same total energy. + +
+ +# How we decode signals +The IR signal is sampled at a **50 µs interval**. For a constant 525 µs pulse or pause we therefore get 10 or 11 samples, each with 50% probability.
+And believe me, if you send a 525 µs signal, your receiver will output something between around 400 and 700 µs!
+Therefore **we decode by default with a +/- 25% margin** using the formulas [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremoteInt.h#L376-L399).
+E.g. for the NEC protocol with its 560 µs unit length, we have TICKS_LOW = 8.358 and TICKS_HIGH = 15.0. This means, we accept any value between 8 ticks / 400 µs and 15 ticks / 750 µs (inclusive) as a mark or as a zero space. For a one space we have TICKS_LOW = 25.07 and TICKS_HIGH = 45.0.
+And since the receivers generated marks are longer or shorter than the spaces, +we have introduced the [`MARK_EXCESS_MICROS`](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library) macro +to compensate for this receiver (and signal strength as well as ambient light dependent :disappointed: ) specific deviation.
+Welcome to the world of **real world signal processing**. + +
+ +# NEC encoding diagrams +Created with sigrok PulseView with IR_NEC decoder by DjordjeMandic.
+8 bit address NEC code +![8 bit address NEC code](https://user-images.githubusercontent.com/6750655/108884951-78e42b80-7607-11eb-9513-b07173a169c0.png) +16 bit address NEC code +![16 bit address NEC code](https://user-images.githubusercontent.com/6750655/108885081-a6c97000-7607-11eb-8d35-274a7065b6c4.png) + +
+ +# Quick comparison of 5 Arduino IR receiving libraries +**This is a short comparison and may not be complete or correct.** + +I created this comparison matrix for [myself](https://github.com/ArminJo) in order to choose a small IR library for my project and to have a quick overview, when to choose which library.
+It is dated from **24.06.2022** and updated 10/2023. If you have complains about the data or request for extensions, please send a PM or open a discussion. + +[Here](https://github.com/crankyoldgit/IRremoteESP8266) you find an **ESP8266/ESP32** version of IRremote with an **[impressive list of supported protocols](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/SupportedProtocols.md)**. + +| Subject | [IRMP](https://github.com/IRMP-org/IRMP) | [IRLremote](https://github.com/NicoHood/IRLremote) | [IRLib2](https://github.com/cyborg5/IRLib2)
**mostly unmaintained** | [IRremote](https://github.com/Arduino-IRremote/Arduino-IRremote) | [TinyIR](https://github.com/Arduino-IRremote/Arduino-IRremote/tree/master/examples/TinyReceiver/TinyReceiver.ino) | [IRsmallDecoder](https://github.com/LuisMiCa/IRsmallDecoder) +|-|-|-|-|-|-|-| +| Number of protocols | **50** | Nec + Panasonic + Hash \* | 12 + Hash \* | 17 + PulseDistance + Hash \* | NEC + FAST | NEC + RC5 + Sony + Samsung | +| Timing method receive | Timer2 or interrupt for pin 2 or 3 | **Interrupt** | Timer2 or interrupt for pin 2 or 3 | Timer2 | **Interrupt** | **Interrupt** | +| Timing method send | PWM and timing with Timer2 interrupts | Timer2 interrupts | Timer2 and blocking wait | PWM with Timer2 and/or blocking wait with delay
Microseconds() | blocking wait with delay
Microseconds() | % | +| Send pins| All | All | All ? | Timer dependent | All | % | +| Decode method | OnTheFly | OnTheFly | RAM | RAM | OnTheFly | OnTheFly | +| Encode method | OnTheFly | OnTheFly | OnTheFly | OnTheFly or RAM | OnTheFly | % | +| Callback support | x | % | % | x | x | % | +| Repeat handling | Receive + Send (partially) | % | ? | Receive + Send | Receive + Send | Receive | +| LED feedback | x | % | x | x | Receive | % | +| FLASH usage (simple NEC example with 5 prints) | 1820
(4300 for 15 main / 8000 for all 40 protocols)
(+200 for callback)
(+80 for interrupt at pin 2+3)| 1270
(1400 for pin 2+3) | 4830 | 1770 | **900** | ?1100? | +| RAM usage | 52
(73 / 100 for 15 (main) / 40 protocols) | 62 | 334 | 227 | **19** | 29 | +| Supported platforms | **avr, megaavr, attiny, Digispark (Pro), esp8266, ESP32, STM32, SAMD 21, Apollo3
(plus arm and pic for non Arduino IDE)** | avr, esp8266 | avr, SAMD 21, SAMD 51 | avr, attiny, [esp8266](https://github.com/crankyoldgit/IRremoteESP8266), esp32, SAM, SAMD | **All platforms with attach
Interrupt()** | **All platforms with attach
Interrupt()** | +| Last library update | 5/2023 | 4/2018 | 11/2022 | 9/2023 | 5/2023 | 2/2022 | +| Remarks | Decodes 40 protocols concurrently.
39 Protocols to send.
Work in progress. | Only one protocol at a time. | Consists of 5 libraries. **Project containing bugs - 63 issues, 10 pull requests.* | Universal decoder and encoder.
Supports **Pronto** codes and sending of raw timing values. | Requires no timer. | Requires no timer. | + +\* The Hash protocol gives you a hash as code, which may be sufficient to distinguish your keys on the remote, but may not work with some protocols like Mitsubishi + +
+ +# [History](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/changelog.md) + +# Useful links +- [List of public IR code databases](http://www.harctoolbox.org/IR-resources.html) +- [LIRC database](http://lirc-remotes.sourceforge.net/remotes-table.html) +- [IRMP list of IR protocols](https://www.mikrocontroller.net/articles/IRMP_-_english#IR_Protocols) +- [IRDB database for IR codes](https://github.com/probonopd/irdb/tree/master/codes) +- [IRP definition files for IR protocols](https://github.com/probonopd/MakeHex/tree/master/protocols) +- [Good introduction to IR remotes by DroneBot Workshop](https://dronebotworkshop.com/ir-remotes/) +- [IR Remote Control Theory and some protocols (upper right hamburger icon)](https://www.sbprojects.net/knowledge/ir/) +- [Interpreting Decoded IR Signals (v2.45)](http://www.hifi-remote.com/johnsfine/DecodeIR.html) +- ["Recording long Infrared Remote control signals with Arduino"](https://www.analysir.com/blog/2014/03/19/air-conditioners-problems-recording-long-infrared-remote-control-signals-arduino) +- The original blog post of Ken Shirriff [A Multi-Protocol Infrared Remote Library for the Arduino](http://www.arcfn.com/2009/08/multi-protocol-infrared-remote-library.html) +- [Vishay datasheet](https://www.vishay.com/docs/80069/circuit.pdf) -We are open to suggestions for adding support to new boards, however we highly recommend you contact your supplier first and ask them to provide support from their side. - -## Hardware specifications -The timer and the pin usage can be adjusted in [IRremoteBoardDefs.h](src/private/IRremoteBoardDefs.h) - -| Board/CPU | IR-Send (PWM) Pin | Timers | -|--------------------------------------------------------------------------|---------------------|-------------------| -| [ATtiny84](https://github.com/SpenceKonde/ATTinyCore) | **6** | **1** | -| [ATtiny85 > 1 MHz](https://github.com/SpenceKonde/ATTinyCore) | **1**, 4 | **0**, 1 | -| [ATmega8](https://github.com/MCUdude/MiniCore) | **9** | **1** | -| [ATmega48, ATmega88, ATmega168, **ATmega328**](https://github.com/MCUdude/MiniCore) | **3**, 9 | 1, **2** | -| [ATmega1284](https://github.com/MCUdude/MightyCore) | 13, 14, 6 | 1, **2**, 3 | -| [ATmega164, ATmega324, ATmega644](https://github.com/MCUdude/MightyCore) | 13, **14** | 1, **2** | -| [ATmega8535 ATmega16, ATmega32](https://github.com/MCUdude/MightyCore) | **13** | **1** | -| [ATmega64, ATmega128, ATmega1281, ATmega2561](https://github.com/MCUdude/MegaCore) | **13** | **1** | -| [ATmega8515, ATmega162](https://github.com/MCUdude/MajorCore) | **13** | **1** | -| ATmega1280, ATmega2560 | 5, 6, **9**, 11, 46 | 1, **2**, 3, 4, 5 | -| ATmega4809 | 5, 6, **9**, 11, 46 | **TCB0** | -| Leonardo (Atmega32u4) | 5, **9**, 13 | 1, 3, **4_HS** | -| Zero (SAMD) | \*, **9** | **TC3** | -| [ESP32](http://esp32.net/) | **4**, all pins | **1** | -| [Sparkfun Pro Micro](https://www.sparkfun.com/products/12640) | **5**, 9 | 1, **3** | -| [Teensy 1.0](https://www.pjrc.com/teensy/) | **17** | **1** | -| [Teensy 2.0](https://www.pjrc.com/teensy/) | **9**, 10, 14 | 1, **3**, 4_HS | -| [Teensy++ 1.0 / 2.0](https://www.pjrc.com/teensy/) | **1**, 16, 25 | 1, **2**, 3 | -| [Teensy 3.0 / 3.1](https://www.pjrc.com/teensy/) | **5** | **CMT** | -| [Teensy-LC](https://www.pjrc.com/teensy/) | **16** | **TPM1** | - -## Revision History -Please see [changelog.md](https://github.com/z3t0/Arduino-IRremote/blob/master/changelog.md). - -## API documentation -See [API reference in wiki](https://github.com/z3t0/Arduino-IRremote/wiki/API-Reference). - -To generate the API documentation, -Doxygen, as well as [Graphviz](http://www.graphviz.org/) should be installed. -(Note that on Windows, it may be necessary to add the Graphviz binary directory -(something like `C:\Program Files\Graphviz2.38\bin`) -to the `PATH` variable manually.) -With Doxygen and Graphviz installed, issue the command -`doxygen` from the command line in the main project directory, which will -generate the API documentation in HTML format. -The just generated `api-doc/index.html` can now be opened in a browser. - -## Contributing -If you want to contribute to this project: -- Report bugs and errors -- Ask for enhancements -- Create issues and pull requests -- Tell other people about this library -- Contribute new protocols - -Check [here](https://github.com/z3t0/Arduino-IRremote/blob/master/Contributing.md) for some guidelines. - -## Contact -Email: zetoslab@gmail.com -Please only email me if it is more appropriate than creating an Issue / PR. I **will** not respond to requests for adding support for particular boards, unless of course you are the creator of the board and would like to cooperate on the project. I will also **ignore** any emails asking me to tell you how to implement your ideas. However, if you have a private inquiry that you would only apply to you and you would prefer it to be via email, by all means. - -## Contributors -Check [here](https://github.com/z3t0/Arduino-IRremote/blob/master/Contributors.md) +# [Contributors](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/Contributors.md) # License -Up to the version 2.7.0 the License is GPLv2. -From the version 2.8.0 the license is the MIT license. +Up to the version 2.7.0, the License is GPLv2. +From the version 2.8.0, the license is the MIT license. -## Copyright -Initially coded 2009 Ken Shirriff http://www.righto.com -Copyright (c) 2016 Rafi Khan \ No newline at end of file +# Copyright +Initially coded 2009 Ken Shirriff http://www.righto.com
+Copyright (c) 2016-2017 Rafi Khan https://rafikhan.io
+Copyright (c) 2020-2024 [Armin Joachimsmeyer](https://github.com/ArminJo) diff --git a/changelog.md b/changelog.md index 4d0e243bb..0ab56fa17 100644 --- a/changelog.md +++ b/changelog.md @@ -1,11 +1,282 @@ +# Changelog +The latest version may not be released! +See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-IRremote/commits/master + +# 4.4.2 +- Support for SAMD51 timer3 if timer 5 is not available (Adafruit ItsyBitsy M4). +- attachInterrupt() on SAMD has a different semantic :-(. See: https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/. +- Fixed overflow handling. +- Improved repeat detection for DistanceWidthProtocol. +- Print of IR frame duration in printIRResultShort(); +- PulseDistanceWidthProtocolConstants now in PROGMEM, this saves 190 bytes RAM for unit test. +- Support for PROGMEM PulseDistanceWidthProtocol data. +- Support duplicated 8 bit address for sendSamsungLG(). + +# 4.4.1 +- Support for ESP core 3.x by akellai. +- restartTimer() now uses variable sMicrosAtLastStopTimer to keep track of uncounted ticks between stopTimer() and restartTimer(). +- Removed functions addTicksToInternalTickCounter() and addMicrosToInternalTickCounter(), which were added in 4.1.0. +- Version 2.2.0 of TinyIR with new TinyReceiverDecode() function to be used as drop in for IrReceiver.decode(). +- Support of RC6A. + +# 4.4.0 +- Using 8 bit raw timing buffer for all timings except frame gap (former rawbuf[0]). +- Renamed decodedIRData.initialGap to decodedIRData.initialGapTicks. +- sendNEC() and sendNEC2() now accepts 16 bit command to better map to NECext protocol found in IRDB databases. +- ir_DistanceWidthProtocol() now decodes up to 10 ms mark or spaces if RAM is bigger than 2 k. +- Improved sensitivity and decoding of PULSE_DISTANCE + PULSE_WIDTH protocols. +- Changed TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING to TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT. +- Improved examples AllProtocolsOnLCD, UnitTest and SimpleReceiver. +- New functions decodePulseDistanceWidthData() with 6 parameters and decodePulseDistanceWidthDataStrict() with 7 parameters. + +# 4.3.2 +- Added sendSonyMSB(unsigned long data, int nbits) as a clone of sendSony(unsigned long data, int nbits) to be more consistent. +- Added sendSamsungMSB(unsigned long data, int nbits) as a clone of sendSAMSUNG(unsigned long data, int nbits) to be more consistent. +- Added ESP32 core 3.x error message. + +# 4.3.1 + - Fixed overflow bug for rawlen > 254. + - Removed deprecated sendPulseDistance... functions with parameter aSendStopBit. + +# 4.3.0 +- Removed default value USE_DEFAULT_FEEDBACK_LED_PIN for last parameter of IRsend::begin(bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin). + Therefore IrSender.begin(DISABLE_LED_FEEDBACK) will not longer work! +- Added convenience function isIRReceiverAttachedForTinyReceiver(). +- Added Extended NEC Protocol macro to TinyIR by Buzzerb. +- Fixed sendSamsung() / sendSamsungLG() bug. +- Added functions stopTimer(), restartTimer() and restartTimerWithTicksToAdd(). +- Added rawlen and initialGap to IRData. +- Added ReceiveAndSendHobToHood example. +- Changed RECORD_GAP_MICROS default value from 5000 to 8000. + +# 4.2.1 +- Fix wrong type of tEnableLEDFeedback in IRSend.hpp and IRReceive.hpp. +- TinyReceiver 2.0 + - New TinyIRReceiverData which is filled with address, command and flags. + - Removed parameters address, command and flags from callback handleReceivedTinyIRData() and printTinyReceiverResultMinimal(). + - Callback function now only enabled if USE_CALLBACK_FOR_TINY_RECEIVER is activated. +- Fix changing IR_SEND_PIN dynamically for ESP32. +- Fix wrong type of tEnableLEDFeedback. +- Support for ESP32-C3. + +# 4.2.0 +- The old decode function is renamed to decode_old(decode_results *aResults). decode (decode_results *aResults) is only available in IRremote.h and prints a message. +- Added DECODE_ONKYO, to force 16 bit command and data decoding. +- Enable Bang&Olufsen 455 kHz if SEND_PWM_BY_TIMER is defined. +- Fixed bug: TinyReceiver throwing ISR not in IRAM on ESP8266. +- Usage of ATTinyCore pin numbering scheme e.g. PIN_PB2. +- Added ARDUINO_ARCH_NRF52 to support Seeed XIAO nRF52840 Sense. +- First untested support of Uno R4. +- Extraced version macros to IRVersion.h. + +## 4.1.2 +- Workaround for ESP32 RTOS delay() timing bug influencing the mark() function. + +## 4.1.1 +- SAMD51 use timer3 if timer5 not available. +- Disabled #define LOCAL_DEBUG in IRReceive.hpp, which was accidently enabled at 4.1.0. + +## 4.1.0 +- Fixed bug in printing durations > 64535 in printIRResultRawFormatted(). +- Narrowed constraints for RC5 RC6 number of bits. +- Changed the first parameter of printTinyReceiverResultMinimal() to &Serial. +- Removed 3 Serial prints for deprecation warnings to fix #1094. +- Version 1.2.0 of TinyIR. Now FAST protocol with 40 ms period and shorter header space. +- Removed field "bool hasStopBit" and parameter "bool aSendStopBit" from PulseDistanceWidthProtocolConstants structure and related functions. +- Changed a lot of "unsigned int" types to "uint16_t" types. +- Improved overflow handling. +- Improved software PWM generation. +- Added FAST protocol. +- Improved handling of PULSE_DISTANCE + PULSE_WIDTH protocols. +- New example ReceiveAndSendDistanceWidth. +- Removed the automatic restarting of the receiver timer after sending with SEND_PWM_BY_TIMER enabled. +- Split ISR into ISR and function IRPinChangeInterruptHandler(). +- Added functions addTicksToInternalTickCounter() and addMicrosToInternalTickCounter(). + +## 4.0.0 +- Added decoding of PulseDistanceWidth protocols and therefore changed function decodeDistance() to decodeDistanceWidth() and filename ir_DistanceProtocol.hpp to ir_DistanceWidthProtocol.hpp. +- Removed static function printIRSendUsage(), but kept class function printIRSendUsage(). +- Changed type of decodedRawData and decodedRawDataArray which is now 64 bit for 32 bit platforms. +- Added receiver callback functionality and registerReceiveCompleteCallback() function. +- Introduced common structure PulseDistanceWidthProtocolConstants. +- Where possible, changed all send and decode functions to use PulseDistanceWidthProtocolConstants. +- Improved MSB/LSB handling +- New convenience functions bitreverse32Bit() and bitreverseOneByte(). +- Improved Magiquest protocol. +- Fix for #1028 - Prevent long delay caused by overflow when frame duration < repeat period - Thanks to Stephen Humphries! +- Support for ATtiny816 - Thanks to elockman. +- Added Bang&Olufsen protocol. #1030. +- Third parameter of function "void begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin)" is not optional anymore and this function is now only available if IR_SEND_PIN is not defined. #1033. +- Fixed bug in sendSony() for command parameter > 0x7F; +- Fixed bug with swapped LG2 header mark and space. +- Disabled strict checks while decoding. They can be enabled by defining DECODE_STRICT_CHECKS. +- Merged the 2 decode pulse width and distance functions. +- Changed macro names _REPEAT_SPACE to _REPEAT_DISTANCE. +- Improved TinyIRReceiver,added FAST protocol for it and added TinyIRSender.hpp and TinySender example, renamed TinyReceiver.h to TinyIR.h. +- Added DISABLE_CODE_FOR_RECEIVER to save program memory and RAM if receiving functionality is not required. +- Extracted protocol functions used by receive and send to IRProtocol.hpp. +- Analyzed Denon code table and therefore changed Denon from MSB to LSB first. +- Renamed sendRC6(aRawData...) to sendRC6Raw( aRawData...). +- Support for seeduino which lacks the print(unsigned long long...) method. Thanks to sklott https://stackoverflow.com/users/11680056/sklott +- Added support for attiny1614 by Joe Ostrander. +- Fixed SEND_PWM_BY_TIMER for ATtiny167 thanks to freskpe. +- Improved SHARP repeat decoding. +- Replaced macros TIMER_EN/DISABLE_RECEIVE_INTR and EN/DISABLE_SEND_PWM_BY_TIMER by functions. +- Added SAMSUNG48 protocol and sendSamsung48() function. + +## 3.9.0 +- Improved documentation with the help of [ElectronicsArchiver}(https://github.com/ElectronicsArchiver). +- Added NEC2 protocol. +- Improved Magiquest protocol. +- Renamed sendSamsungRepeat() to sendSamsungLGRepeat(). +- Added function sendPulseDistanceWidth(). +- Improved repeat detection for some protocols. + +## 3.8.0 +- Changed Samsung repeat handling. Old handling is available as SamsungLG. +- Added function printIRSendUsage(). +- Reduced output size and improved format of printIRResultRawFormatted() to fasten up output (and getting repeats properly decoded). +- Fixed Bug in sendDenonRaw() and improved decodeDenon(). +- Fixed potential bug in SendBiphase data for 1 bit. +- Fixed bug in send for RP4020. +- Fixed pin mapping problems especially for Teensy 2.0. +- Added support for decoding of "special" NEC repeats. +- Added SAMD51 support. +- Improved pin mapping for TinyReceiver. + +## 3.7.1 +- SendRaw now supports bufferlenght > 255. +- Improved DistanceProtocol decoder output. +- Fixed ESP32 send bug for 2.x ESP32 cores. + +## 3.7.0 +- Changed TOLERANCE to TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING and documented it. +- Changed last uint8_t to uint_fast8_t and uint16_t to unsigned integer. +- Improved MagiQuest protocol. +- Improved prints and documentation. +- Added IrReceiver.restartAfterSend() and inserted it in every send(). Closes #989 +- Use IRAM_ATTR instead of deprecated ICACHE_RAM_ATTR for ESP8266. +- Removed pulse width decoding from ir_DistanceProtocol. + +## 3.6.1 +- Switched Bose internal protocol timing for 0 and 1 -> old 1 timing is now 0 and vice versa. + +## 3.6.0 +- Separated enable flag of send and receive feedback LED. Inspired by PR#970 from luvaihassanali. +- RP2040 support added. +- Refactored IRTimer.hpp. +- Refactored IR_SEND_PIN and IrSender.sendPin handling. +- Renamed IR_SEND_DUTY_CYCLE to IR_SEND_DUTY_CYCLE_PERCENT. +- Fixed bugs for SEND_PWM_BY_TIMER active. + +## 3.5.2 +- Improved support for Teensy boards by Paul Stoffregen. + +## 3.5.1 +- Renamed INFO_PRINT to IR_INFO_PRINT as well as for DEBUG and TRACE. +- Fixed error with DEBUG in TinyIRReceiver.hpp. +- Support for ATmega88 see issue #923. Thanks to Dolmant. +- NO_LED_FEEDBACK_CODE replaces and extends DISABLE_LED_FEEDBACK_FOR_RECEIVE. +- Removed NO_LEGACY_COMPATIBILITY macro, it was useless now. +- Fix ESP32 send bug see issue #927. + +## 3.5.0 +- Improved ir_DistanceProtocol. +- Tone for ESP32. +- last phase renamed *.cpp.h to .hpp. +- No deprecation print for ATtinies. +- Renamed ac_LG.cpp to ac_LG.hpp. +- Maintained MagiQuest by E. Stuart Hicks. +- Improved print Pronto by Asuki Kono. +- Added printActiveIRProtocols() function. +- Used IR_SEND_PIN to reduce code size and improved send timing for AVR. + +## 3.4.0 +- Added LG2 protocol. +- Added ATtiny167 (Digispark Pro) support. +- Renamed *.cpp.h to .hpp. +- organized carrier frequencies. +- Compiler switch USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN added. +- Moved blink13() back to IRrecv class. +- Added Kaseikyo convenience functions like sendKaseikyo_Denon(). +- Improved / adjusted LG protocol and added class Aircondition_LG based on real hardware supplied by makerspace 201 (https://wiki.hackerspaces.org/ZwoNullEins) from Cologne. +- Improved universal decoder for pulse distance protocols to support more than 32 bits. +- Added mbed support. + +## 3.3.0 +- Fix errors if LED_BUILTIN is not defined. +- Fixed error for AVR timer1. Thanks to alexbarcelo. +- New example IRremoteExtensionTest. +- Enabled megaAVR 0-series devices. +- Added universal decoder for pulse distance protocols. + +## 3.2.0 +- Fix for ESP32 send Error, removed `USE_SOFT_SEND_PWM` macro. +- Added Onkyo protocol. +- Support for old 2.x code by backwards compatible `decode(decode_results *aResults)` function. +- Removed USE_OLD_DECODE macro and added NO_LEGACY_COMPATIBILITY macro. +- Added ATtiny1604 support. +- New SendAndReceive example. +- Added ESP8266 support. +- Extended DEBUG output. + +## 3.1.0 +- Generation of PWM by software is active by default. +- Removed decode_results results. +- Renamed most irparams_struct values. +- Fixed LG send bug and added unit test. +- Replaced `#define DECODE_NEC 1/0` by defining/not defining. +- Use LED_BUILTIN instead of FEEDBACK_LED if FeedbackLEDPin is 0. +- Use F_CPU instead of SYSCLOCK. +- Removed SENDPIN_ON and SENDPIN_OFF macros. + +- Refactored board specific code for timer and feedback LED. +- Extracted common LED feedback functions and implemented feedback for send. +- MATCH_MARK() etc. now available as matchMark(). +- Added STM32F1 by (by Roger Clark) support. +- Added stm32 (by ST) support. Thanks to Paolo Malaspina. +- Added ATtiny88 support. + +## 3.0.2 +- Bug fix for USE_OLD_DECODE. +- Increase RECORD_GAP_MICROS to 11000. +- Fix overflow message. (#793). +- Improved handling for HASH decoder. +- Tested for ATtiny85. +- Added `printIRResultMinimal()`. +- Added missing IRAM_ATTR for ESP32. +- Adapted to TinyCore 0.0.7. +- Fixed decodeSony 20 bit bug #811. +- Replaced delayMicroseconds with customDelayMicroseconds and removed NoInterrupt() for send functions, removed SPIN_WAIT macro, sleepMicros() and sleepUntilMicros(). +- Fixed LG checksum error. +- Fixed JVC repeat error. + +## 3.0.0 + 3.0.1 2021/02 +- New LSB first decoders are default. +- Added SendRaw with byte data. +- Fixed resume bug if irparams.rawlen >= RAW_BUFFER_LENGTH. Thanks to Iosif Peterfi +- Added `dumpPronto(String *aString, unsigned int frequency)` with String object as argument. Thanks to Iosif Peterfi +- Removed Test2 example. +- Fixed swapped cases in `getProtocolString()`. Thanks to Jim-2249 +- Added compile option `IR_INPUT_IS_ACTIVE_HIGH`. Thanks to Jim-2249 +- Corrected template. Thanks to Jim-2249 +- Introduced standard decode and send functions. +- Added compatibility with tone for AVR's. +- New TinyIRreceiver does not require a timer. +- New MinimalReceiver and IRDispatcherDemo examples. +- Added TinyCore 32 / ATtiny3217 support. +- Added Apple protocol. + +## 2.8.1 2020/10 +- Fixed bug in Sony decode introduced in 2.8.0. + ## 2.8.0 2020/10 -- Changed License to MIT see https://github.com/z3t0/Arduino-IRremote/issues/397. +- Changed License to MIT see https://github.com/Arduino-IRremote/Arduino-IRremote/issues/397. - Added ATtiny timer 1 support. - Changed wrong return code signature of decodePulseDistanceData() and its handling. -- Removed Mitsubishi protocol, since the implementation is in contradiction with all documentation I could found and therefore supposed to be wrong. -- Removed AIWA implementation, since it is only for 1 device and at least sending implemented wrong. +- Removed Mitsubishi protocol, since the implementation is in contradiction with all documentation I found and therefore supposed to be wrong. +- Removed AIWA implementation, since it is only for 1 device and at least the sending was implemented wrong. - Added Lego_PF decode. -- Added new example IR2Keyboard. - Changed internal usage of custom_delay_usec. - Moved dump/print functions from example to irReceiver. - irPronto.cpp: Using Print instead of Stream saves 1020 bytes program memory. Changed from & to * parameter type to be more transparent and consistent with other code of IRremote. @@ -19,8 +290,8 @@ - Optimized types in sendRC5ext and sendSharpAlt. - Added `DECODE_NEC_STANDARD` and `SEND_NEC_STANDARD`. - Renamed all IRrecv* examples to IRreceive*. -- Added functions `printResultShort(&Serial)` and `getProtocolString(decode_type_t aDecodeType)`. -- Added flag `results.isRepeat`. +- Added functions `printIRResultShort(&Serial)` and `getProtocolString(decode_type_t aDecodeType)`. +- Added flag `decodedIRData.isRepeat`. - Updated examples. ## 2.6.1 2020/08 @@ -33,7 +304,7 @@ - NEC repeat implementation. - Formatting and changing `TIMER_CONFIG_KHZ` and `TIMER_CONFIG_NORMAL` macros to static functions. - Added `IRAM_ATTR` for ESP32 ISR. -- Removed #define `HAS_AVR_INTERRUPT_H`. +- Removed `#define HAS_AVR_INTERRUPT_H`. - Changed Receiver States. Now starting with 0. - Changed switch to if / else if in IRRemote.cpp because of ESP32 compiler bug. - Changed `DEBUG` handling since compiler warns about empty "IF" or "ELSE" statements in IRRemote.cpp. @@ -42,7 +313,7 @@ - Corrected keywords.txt. - BoseWave protocol added PR #690. - Formatting comply to the new stylesheet. -- Renamed "boarddefs.h" [ISSUE #375](https://github.com/z3t0/Arduino-IRremote/issues/375). +- Renamed "boarddefs.h". - Renamed `SEND_PIN` to `IR_SEND_PIN`. - Renamed state macros. - Enabled `DUTY_CYCLE` for send signal. @@ -52,25 +323,25 @@ Changes from #283 by bengtmartensson - Added function sendRaw_P() for sending data from flash. Changes from #268 by adamlhumphreys -- Optimized by reducing floating point operations as suggested by @madmalkav (#193). +- Optimized by reducing floating point operations as suggested by madmalkav (#193). - Optimized with macros when using default `MICROS_PER_TICK` and `TOLERANCE`. - Made decodeHash as a settable protocol defined by `DECODE_HASH`. -- Added Philips Extended RC-5 protocol support [PR #522] (https://github.com/z3t0/Arduino-IRremote/pull/522) +- Added Philips Extended RC-5 protocol support [PR #522] (https://github.com/Arduino-IRremote/Arduino-IRremote/pull/522) ## 2.4.0 - 2017/08/10 - - Cleanup of hardware dependencies. Merge in SAM support [PR #437](https://github.com/z3t0/Arduino-IRremote/pull/437) + - Cleanup of hardware dependencies. Merge in SAM support [PR #437](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/437) ## 2.3.3 - 2017/03/31 -- Added ESP32 IR receive support [PR #427](https://github.com/z3t0/Arduino-IRremote/pull/425) +- Added ESP32 IR receive support [PR #427](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/425) ## 2.2.3 - 2017/03/27 -- Fix calculation of pause length in LEGO PF protocol [PR #427](https://github.com/z3t0/Arduino-IRremote/pull/427) +- Fix calculation of pause length in LEGO PF protocol [PR #427](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/427) ## 2.2.2 - 2017/01/20 -- Fixed naming bug [PR #398](https://github.com/z3t0/Arduino-IRremote/pull/398) +- Fixed naming bug [PR #398](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/398) ## 2.2.1 - 2016/07/27 -- Added tests for Lego Power Functions Protocol [PR #336](https://github.com/z3t0/Arduino-IRremote/pull/336) +- Added tests for Lego Power Functions Protocol [PR #336](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/336) ## 2.2.0 - 2016/06/28 - Added support for ATmega8535 @@ -83,23 +354,23 @@ Changes from #268 by adamlhumphreys - Added support for ATmega64 - Added support for ATmega128 -[PR](https://github.com/z3t0/Arduino-IRremote/pull/324) +[PR](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/324) ## 2.1.1 - 2016/05/04 -- Added Lego Power Functions Protocol [PR #309](https://github.com/z3t0/Arduino-IRremote/pull/309) +- Added Lego Power Functions Protocol [PR #309](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/309) ## 2.1.0 - 2016/02/20 -- Improved Debugging [PR #258](https://github.com/z3t0/Arduino-IRremote/pull/258) -- Display TIME instead of TICKS [PR #258](https://github.com/z3t0/Arduino-IRremote/pull/258) +- Improved Debugging [PR #258](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/258) +- Display TIME instead of TICKS [PR #258](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/258) ## 2.0.4 - 2016/02/20 -- Add Panasonic and JVC to IRrecord example [PR](https://github.com/z3t0/Arduino-IRremote/pull/54) +- Add Panasonic and JVC to IRrecord example [PR](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/54) ## 2.0.3 - 2016/02/20 -- Change IRSend Raw parameter to const [PR](https://github.com/z3t0/Arduino-IRremote/pull/227) +- Change IRSend Raw parameter to const [PR](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/227) ## 2.0.2 - 2015/12/02 -- Added IRremoteInfo Sketch - [PR](https://github.com/z3t0/Arduino-IRremote/pull/241) +- Added IRremoteInfo Sketch - [PR](https://github.com/Arduino-IRremote/Arduino-IRremote/pull/241) - Enforcing changelog.md ## 2.0.1 - 2015/07/26 - [Release](https://github.com/shirriff/Arduino-IRremote/releases/tag/BETA) @@ -118,13 +389,13 @@ Changes from #268 by adamlhumphreys - Fixed and Improved many coding errors - Fixed Aiwa RC-T501 Decoding - Fixed Interrupt on ATmega8 -- Switched to Stable Release of @PlatformIO +- Switched to Stable Release of PlatformIO ### Additions - Added Aiwa RC-T501 Protocol - Added Denon Protocol - Added Pronto Support -- Added Library Properties +- Added compile options - Added Template For New Protocols - Added this changelog - Added Teensy LC Support diff --git a/examples/AllProtocolsOnLCD/ADCUtils.h b/examples/AllProtocolsOnLCD/ADCUtils.h new file mode 100644 index 000000000..33c2bbfc1 --- /dev/null +++ b/examples/AllProtocolsOnLCD/ADCUtils.h @@ -0,0 +1,237 @@ +/* + * ADCUtils.h + * + * Copyright (C) 2016-2022 Armin Joachimsmeyer + * Email: armin.joachimsmeyer@gmail.com + * + * This file is part of Arduino-Utils https://github.com/ArminJo/Arduino-Utils. + * + * ArduinoUtils is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef _ADC_UTILS_H +#define _ADC_UTILS_H + +#include + +#if defined(__AVR__) && defined(ADCSRA) && defined(ADATE) && (!defined(__AVR_ATmega4809__)) +#define ADC_UTILS_ARE_AVAILABLE + +// External Reference Current is 150 uA for 5 V and 100 uA for 3.5 V +#define READING_FOR_AREF 1024L // Datasheet 24.2: The minimum value represents GND and the maximum value represents the voltage on the AREF pin minus 1 LSB + +// PRESCALE4 => 13 * 4 = 52 microseconds per ADC conversion at 1 MHz Clock => 19,2 kHz +#define ADC_PRESCALE2 1 // 26 microseconds per ADC conversion at 1 MHz +#define ADC_PRESCALE4 2 // 52 microseconds per ADC conversion at 1 MHz +// PRESCALE8 => 13 * 8 = 104 microseconds per ADC sample at 1 MHz Clock => 9,6 kHz +#define ADC_PRESCALE8 3 // 104 microseconds per ADC conversion at 1 MHz +#define ADC_PRESCALE16 4 // 13/208 microseconds per ADC conversion at 16/1 MHz - degradations in linearity at 16 MHz +#define ADC_PRESCALE32 5 // 26/416 microseconds per ADC conversion at 16/1 MHz - very good linearity at 16 MHz +#define ADC_PRESCALE64 6 // 52 microseconds per ADC conversion at 16 MHz +#define ADC_PRESCALE128 7 // 104 microseconds per ADC conversion at 16 MHz --- Arduino default + +// definitions for 0.1 ms conversion time +#if (F_CPU == 1000000) +#define ADC_PRESCALE ADC_PRESCALE8 +#elif (F_CPU == 8000000) +#define ADC_PRESCALE ADC_PRESCALE64 +#elif (F_CPU == 16000000) +#define ADC_PRESCALE ADC_PRESCALE128 +#endif + +/* + * Reference shift values are complicated for ATtinyX5 since we have the extra register bit REFS2 + * in ATTinyCore, this bit is handled programmatical and therefore the defines are different. + * To keep my library small, I use the changed defines. + * After including this file you can not call the ATTinyCore readAnalog functions reliable, if you specify references other than default! + */ +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) +// defines are for ADCUtils.cpp, they can be used WITHOUT bit reordering +#undef DEFAULT +#undef EXTERNAL +#undef INTERNAL1V1 +#undef INTERNAL +#undef INTERNAL2V56 +#undef INTERNAL2V56_EXTCAP + +#define DEFAULT 0 +#define EXTERNAL 4 +#define INTERNAL1V1 8 +#define INTERNAL INTERNAL1V1 +#define INTERNAL2V56 9 +#define INTERNAL2V56_EXTCAP 13 + +#define SHIFT_VALUE_FOR_REFERENCE REFS2 +#define MASK_FOR_ADC_REFERENCE (_BV(REFS0) | _BV(REFS1) | _BV(REFS2)) +#define MASK_FOR_ADC_CHANNELS (_BV(MUX0) | _BV(MUX1) | _BV(MUX2) | _BV(MUX3)) +#else // AVR_ATtiny85 + +#define SHIFT_VALUE_FOR_REFERENCE REFS0 +#define MASK_FOR_ADC_REFERENCE (_BV(REFS0) | _BV(REFS1)) +#define MASK_FOR_ADC_CHANNELS (_BV(MUX0) | _BV(MUX1) | _BV(MUX2) | _BV(MUX3)) +#endif + +// Temperature channel definitions - 1 LSB / 1 degree Celsius +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) +#define ADC_TEMPERATURE_CHANNEL_MUX 15 +#define ADC_1_1_VOLT_CHANNEL_MUX 12 +#define ADC_GND_CHANNEL_MUX 13 +#define ADC_CHANNEL_MUX_MASK 0x0F + +#elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +#define ADC_ISCR_CHANNEL_MUX 3 +#define ADC_TEMPERATURE_CHANNEL_MUX 11 +#define ADC_1_1_VOLT_CHANNEL_MUX 12 +#define ADC_GND_CHANNEL_MUX 14 +#define ADC_VCC_4TH_CHANNEL_MUX 13 +#define ADC_CHANNEL_MUX_MASK 0x1F + +#elif defined(__AVR_ATmega328P__) +#define ADC_TEMPERATURE_CHANNEL_MUX 8 +#define ADC_1_1_VOLT_CHANNEL_MUX 14 +#define ADC_GND_CHANNEL_MUX 15 +#define ADC_CHANNEL_MUX_MASK 0x0F + +#elif defined(__AVR_ATmega644P__) +#define ADC_TEMPERATURE_CHANNEL_MUX // not existent +#define ADC_1_1_VOLT_CHANNEL_MUX 0x1E +#define ADC_GND_CHANNEL_MUX 0x1F +#define ADC_CHANNEL_MUX_MASK 0x0F + +#elif defined(__AVR_ATmega32U4__) +#define ADC_TEMPERATURE_CHANNEL_MUX 0x27 +#define ADC_1_1_VOLT_CHANNEL_MUX 0x1E +#define ADC_GND_CHANNEL_MUX 0x1F +#define ADC_CHANNEL_MUX_MASK 0x3F + +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) +#define ADC_1_1_VOLT_CHANNEL_MUX 0x1E +#define ADC_GND_CHANNEL_MUX 0x1F +#define ADC_CHANNEL_MUX_MASK 0x1F + +#define INTERNAL INTERNAL1V1 + +#else +#error "No temperature channel definitions specified for this AVR CPU" +#endif + +/* + * Thresholds for OVER and UNDER voltage and detection of kind of power supply (USB or Li-ion) + * + * Default values are suitable for Li-ion batteries. + * We normally have voltage drop at the connectors, so the battery voltage is assumed slightly higher, than the Arduino VCC. + * But keep in mind that the ultrasonic distance module HC-SR04 may not work reliable below 3.7 volt. + */ +#if !defined(LI_ION_VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) +#define LI_ION_VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT 3400 // Do not stress your battery and we require some power for standby +#endif +#if !defined(LI_ION_VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) +#define LI_ION_VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT 3000 // Many Li-ions are specified down to 3.0 volt +#endif + +#if !defined(VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) +#define VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT LI_ION_VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT +#endif +#if !defined(VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) +#define VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT LI_ION_VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT +#endif +#if !defined(VCC_OVERVOLTAGE_THRESHOLD_MILLIVOLT) +#define VCC_OVERVOLTAGE_THRESHOLD_MILLIVOLT 5250 // + 5 % operation voltage +#endif +#if !defined(VCC_EMERGENCY_OVERVOLTAGE_THRESHOLD_MILLIVOLT) +#define VCC_EMERGENCY_OVERVOLTAGE_THRESHOLD_MILLIVOLT 5500 // +10 %. Max recommended operation voltage +#endif +#if !defined(VCC_CHECK_PERIOD_MILLIS) +#define VCC_CHECK_PERIOD_MILLIS 10000L // 10 seconds period of VCC checks +#endif +#if !defined(VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP) +#define VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP 6 // Shutdown after 6 times (60 seconds) VCC below VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT or 1 time below VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT +#endif + +#if !defined(VOLTAGE_USB_POWERED_LOWER_THRESHOLD_MILLIVOLT) +#define VOLTAGE_USB_POWERED_LOWER_THRESHOLD_MILLIVOLT 4300 // Assume USB powered above this voltage +#endif + +#if !defined(VOLTAGE_USB_POWERED_UPPER_THRESHOLD_MILLIVOLT) +#define VOLTAGE_USB_POWERED_UPPER_THRESHOLD_MILLIVOLT 4950 // Assume USB powered below this voltage, because of the loss in USB cable. If we have > 4950, we assume to be powered by VIN. +// In contrast to e.g. powered by VIN, which results in almost perfect 5 volt supply +#endif + +extern long sLastVCCCheckMillis; +extern uint8_t sVCCTooLowCounter; + +uint16_t readADCChannel(uint8_t aADCChannelNumber); +uint16_t readADCChannelWithReference(uint8_t aADCChannelNumber, uint8_t aReference); +uint16_t waitAndReadADCChannelWithReference(uint8_t aADCChannelNumber, uint8_t aReference); +uint16_t waitAndReadADCChannelWithReferenceAndRestoreADMUXAndReference(uint8_t aADCChannelNumber, uint8_t aReference); +uint16_t readADCChannelWithOversample(uint8_t aADCChannelNumber, uint8_t aOversampleExponent); +void setADCChannelAndReferenceForNextConversion(uint8_t aADCChannelNumber, uint8_t aReference); +uint16_t readADCChannelWithReferenceOversampleFast(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aOversampleExponent); +uint32_t readADCChannelMultiSamples(uint8_t aPrescale, uint16_t aNumberOfSamples); +uint16_t readADCChannelMultiSamplesWithReference(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aNumberOfSamples); +uint32_t readADCChannelMultiSamplesWithReferenceAndPrescaler(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aPrescale, + uint16_t aNumberOfSamples); +uint16_t readADCChannelWithReferenceMax(uint8_t aADCChannelNumber, uint8_t aReference, uint16_t aNumberOfSamples); +uint16_t readADCChannelWithReferenceMaxMicros(uint8_t aADCChannelNumber, uint8_t aReference, uint16_t aMicrosecondsToAquire); +uint16_t readUntil4ConsecutiveValuesAreEqual(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aDelay, + uint8_t aAllowedDifference, uint8_t aMaxRetries); + +uint8_t checkAndWaitForReferenceAndChannelToSwitch(uint8_t aADCChannelNumber, uint8_t aReference); + +/* + * readVCC*() functions store the result in sVCCVoltageMillivolt or sVCCVoltage + */ +float getVCCVoltageSimple(void); +void readVCCVoltageSimple(void); +void readVCCVoltageMillivoltSimple(void); +void readVCCVoltage(void); +uint16_t getVCCVoltageMillivolt(void); +void readVCCVoltageMillivolt(void); +uint16_t getVCCVoltageReadingFor1_1VoltReference(void); +uint16_t printVCCVoltageMillivolt(Print *aSerial); +void readAndPrintVCCVoltageMillivolt(Print *aSerial); + +uint16_t getVoltageMillivolt(uint16_t aVCCVoltageMillivolt, uint8_t aADCChannelForVoltageMeasurement); +uint16_t getVoltageMillivolt(uint8_t aADCChannelForVoltageMeasurement); +uint16_t getVoltageMillivoltWith_1_1VoltReference(uint8_t aADCChannelForVoltageMeasurement); +float getCPUTemperatureSimple(void); +float getCPUTemperature(void); +float getTemperature(void) __attribute__ ((deprecated ("Renamed to getCPUTemperature()"))); // deprecated + +bool isVCCUSBPowered(); +bool isVCCUSBPowered(Print *aSerial); +bool isVCCUndervoltageMultipleTimes(); +void resetCounterForVCCUndervoltageMultipleTimes(); +bool isVCCUndervoltage(); +bool isVCCEmergencyUndervoltage(); +bool isVCCOvervoltage(); +bool isVCCOvervoltageSimple(); // Version using readVCCVoltageMillivoltSimple() +bool isVCCTooHighSimple(); // Version not using readVCCVoltageMillivoltSimple() + +#endif // defined(__AVR__) ... + +/* + * Variables and functions defined as dummies to allow for seamless compiling on non AVR platforms + */ +extern float sVCCVoltage; +extern uint16_t sVCCVoltageMillivolt; + +uint16_t readADCChannelWithReferenceOversample(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aOversampleExponent); + +uint16_t getVCCVoltageMillivoltSimple(void); +float getVCCVoltage(void); +float getCPUTemperature(void); + +#endif // _ADC_UTILS_H diff --git a/examples/AllProtocolsOnLCD/ADCUtils.hpp b/examples/AllProtocolsOnLCD/ADCUtils.hpp new file mode 100644 index 000000000..8d0e5e799 --- /dev/null +++ b/examples/AllProtocolsOnLCD/ADCUtils.hpp @@ -0,0 +1,820 @@ +/* + * ADCUtils.hpp + * + * ADC utility functions. Conversion time is defined as 0.104 milliseconds for 16 MHz Arduinos in ADCUtils.h. + * + * Copyright (C) 2016-2023 Armin Joachimsmeyer + * Email: armin.joachimsmeyer@gmail.com + * + * This file is part of Arduino-Utils https://github.com/ArminJo/Arduino-Utils. + * + * ArduinoUtils is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _ADC_UTILS_HPP +#define _ADC_UTILS_HPP + +#include "ADCUtils.h" +#if defined(ADC_UTILS_ARE_AVAILABLE) // set in ADCUtils.h, if supported architecture was detected + +#if !defined(STR) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif + +/* + * By replacing this value with the voltage you measured a the AREF pin after a conversion + * with INTERNAL you can calibrate your ADC readout. For my Nanos I measured e.g. 1060 mV and 1093 mV. + */ +#if !defined(ADC_INTERNAL_REFERENCE_MILLIVOLT) +#define ADC_INTERNAL_REFERENCE_MILLIVOLT 1100L // Change to value measured at the AREF pin. If value > real AREF voltage, measured values are > real values +#endif + +// Union to speed up the combination of low and high bytes to a word +// it is not optimal since the compiler still generates 2 unnecessary moves +// but using -- value = (high << 8) | low -- gives 5 unnecessary instructions +union WordUnionForADCUtils { + struct { + uint8_t LowByte; + uint8_t HighByte; + } UByte; + uint16_t UWord; + int16_t Word; + uint8_t *BytePointer; +}; + +/* + * Enable this to see information on each call. + * Since there should be no library which uses Serial, it should only be enabled for development purposes. + */ +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/* + * Persistent storage for VCC value + */ +float sVCCVoltage; +uint16_t sVCCVoltageMillivolt; + +// for isVCCTooLowMultipleTimes() +long sLastVCCCheckMillis; +uint8_t sVCCTooLowCounter = 0; + +/* + * Conversion time is defined as 0.104 milliseconds by ADC_PRESCALE in ADCUtils.h. + */ +uint16_t readADCChannel(uint8_t aADCChannelNumber) { + WordUnionForADCUtils tUValue; + ADMUX = aADCChannelNumber | (DEFAULT << SHIFT_VALUE_FOR_REFERENCE); + + // ADCSRB = 0; // Only active if ADATE is set to 1. + // ADSC-StartConversion ADIF-Reset Interrupt Flag - NOT free running mode + ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADIF) | ADC_PRESCALE); + + // wait for single conversion to finish + loop_until_bit_is_clear(ADCSRA, ADSC); + + // Get value + tUValue.UByte.LowByte = ADCL; + tUValue.UByte.HighByte = ADCH; + return tUValue.UWord; + // return ADCL | (ADCH <<8); // needs 4 bytes more +} + +/* + * Conversion time is defined as 0.104 milliseconds by ADC_PRESCALE in ADCUtils.h. + */ +uint16_t readADCChannelWithReference(uint8_t aADCChannelNumber, uint8_t aReference) { + WordUnionForADCUtils tUValue; + ADMUX = aADCChannelNumber | (aReference << SHIFT_VALUE_FOR_REFERENCE); + + // ADCSRB = 0; // Only active if ADATE is set to 1. + // ADSC-StartConversion ADIF-Reset Interrupt Flag - NOT free running mode + ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADIF) | ADC_PRESCALE); + + // wait for single conversion to finish + loop_until_bit_is_clear(ADCSRA, ADSC); + + // Get value + tUValue.UByte.LowByte = ADCL; + tUValue.UByte.HighByte = ADCH; + return tUValue.UWord; +} + +/* + * Conversion time is defined as 0.104 milliseconds by ADC_PRESCALE in ADCUtils.h. + * Does NOT restore ADMUX after reading + */ +uint16_t waitAndReadADCChannelWithReference(uint8_t aADCChannelNumber, uint8_t aReference) { + checkAndWaitForReferenceAndChannelToSwitch(aADCChannelNumber, aReference); + return readADCChannelWithReference(aADCChannelNumber, aReference); +} + +/* + * Conversion time is defined as 0.104 milliseconds by ADC_PRESCALE in ADCUtils.h. + * Restores ADMUX after reading + */ +uint16_t waitAndReadADCChannelWithReferenceAndRestoreADMUXAndReference(uint8_t aADCChannelNumber, uint8_t aReference) { + uint8_t tOldADMUX = checkAndWaitForReferenceAndChannelToSwitch(aADCChannelNumber, aReference); + uint16_t tResult = readADCChannelWithReference(aADCChannelNumber, aReference); + checkAndWaitForReferenceAndChannelToSwitch(tOldADMUX & MASK_FOR_ADC_CHANNELS, tOldADMUX >> SHIFT_VALUE_FOR_REFERENCE); + return tResult; +} + +/* + * To prepare reference and ADMUX for next measurement + */ +void setADCChannelAndReferenceForNextConversion(uint8_t aADCChannelNumber, uint8_t aReference) { + ADMUX = aADCChannelNumber | (aReference << SHIFT_VALUE_FOR_REFERENCE); +} + +/* + * @return original ADMUX register content for optional later restoring values + * All experimental values are acquired by using the ADCSwitchingTest example from this library + */ +uint8_t checkAndWaitForReferenceAndChannelToSwitch(uint8_t aADCChannelNumber, uint8_t aReference) { + uint8_t tOldADMUX = ADMUX; + /* + * Must wait >= 7 us if reference has to be switched from 1.1 volt/INTERNAL to VCC/DEFAULT (seen on oscilloscope) + * This is done after the 2 ADC clock cycles required for Sample & Hold :-) + * + * Must wait >= 7600 us for Nano board >= 6200 for Uno board if reference has to be switched from VCC/DEFAULT to 1.1 volt/INTERNAL + * Must wait >= 200 us if channel has to be switched to 1.1 volt internal channel if S&H was at 5 Volt + */ + uint8_t tNewReference = (aReference << SHIFT_VALUE_FOR_REFERENCE); + ADMUX = aADCChannelNumber | tNewReference; +#if defined(INTERNAL2V56) + if ((tOldADMUX & MASK_FOR_ADC_REFERENCE) != tNewReference && (aReference == INTERNAL || aReference == INTERNAL2V56)) { +#else + if ((tOldADMUX & MASK_FOR_ADC_REFERENCE) != tNewReference && aReference == INTERNAL) { +#endif +#if defined(LOCAL_DEBUG) + Serial.println(F("Switch from DEFAULT to INTERNAL")); +#endif + /* + * Switch reference from DEFAULT to INTERNAL + */ + delayMicroseconds(8000); // experimental value is >= 7600 us for Nano board and 6200 for Uno board + } else if ((tOldADMUX & ADC_CHANNEL_MUX_MASK) != aADCChannelNumber) { + if (aADCChannelNumber == ADC_1_1_VOLT_CHANNEL_MUX) { + /* + * Internal 1.1 Volt channel requires <= 200 us for Nano board + */ + delayMicroseconds(350); // 350 was ok and 300 was too less for UltimateBatteryTester - result was 226 instead of 225 + } else { + /* + * 100 kOhm requires < 100 us, 1 MOhm requires 120 us S&H switching time + */ + delayMicroseconds(120); // experimental value is <= 1100 us for Nano board + } + } + return tOldADMUX; +} + +/* + * Oversample and multiple samples only makes sense if you expect a noisy input signal + * It does NOT increase the precision of the measurement, since the ADC has insignificant noise + */ +uint16_t readADCChannelWithOversample(uint8_t aADCChannelNumber, uint8_t aOversampleExponent) { + return readADCChannelWithReferenceOversample(aADCChannelNumber, DEFAULT, aOversampleExponent); +} + +/* + * Conversion time is defined as 0.104 milliseconds by ADC_PRESCALE in ADCUtils.h. + */ +uint16_t readADCChannelWithReferenceOversample(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aOversampleExponent) { + uint16_t tSumValue = 0; + ADMUX = aADCChannelNumber | (aReference << SHIFT_VALUE_FOR_REFERENCE); + + ADCSRB = 0; // Free running mode. Only active if ADATE is set to 1. + // ADSC-StartConversion ADATE-AutoTriggerEnable ADIF-Reset Interrupt Flag + ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIF) | ADC_PRESCALE); + + uint8_t tCount = _BV(aOversampleExponent); + for (uint8_t i = 0; i < tCount; i++) { + /* + * wait for free running conversion to finish. + * Do not wait for ADSC here, since ADSC is only low for 1 ADC Clock cycle on free running conversion. + */ + loop_until_bit_is_set(ADCSRA, ADIF); + + ADCSRA |= _BV(ADIF); // clear bit to enable recognizing next conversion has finished + // Add value + tSumValue += ADCL | (ADCH << 8); // using WordUnionForADCUtils does not save space here + // tSumValue += (ADCH << 8) | ADCL; // this does NOT work! + } + ADCSRA &= ~_BV(ADATE); // Disable auto-triggering (free running mode) + // return rounded value + return ((tSumValue + (tCount >> 1)) >> aOversampleExponent); +} + +/* + * Use ADC_PRESCALE32 which gives 26 us conversion time and good linearity for 16 MHz Arduino + */ +uint16_t readADCChannelWithReferenceOversampleFast(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aOversampleExponent) { + uint16_t tSumValue = 0; + ADMUX = aADCChannelNumber | (aReference << SHIFT_VALUE_FOR_REFERENCE); + + ADCSRB = 0; // Free running mode. Only active if ADATE is set to 1. + // ADSC-StartConversion ADATE-AutoTriggerEnable ADIF-Reset Interrupt Flag + ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIF) | ADC_PRESCALE32); + + uint8_t tCount = _BV(aOversampleExponent); + for (uint8_t i = 0; i < tCount; i++) { + /* + * wait for free running conversion to finish. + * Do not wait for ADSC here, since ADSC is only low for 1 ADC Clock cycle on free running conversion. + */ + loop_until_bit_is_set(ADCSRA, ADIF); + + ADCSRA |= _BV(ADIF); // clear bit to enable recognizing next conversion has finished + // Add value + tSumValue += ADCL | (ADCH << 8); // using WordUnionForADCUtils does not save space here + // tSumValue += (ADCH << 8) | ADCL; // this does NOT work! + } + ADCSRA &= ~_BV(ADATE); // Disable auto-triggering (free running mode) + return ((tSumValue + (tCount >> 1)) >> aOversampleExponent); +} + +/* + * Returns sum of all sample values + * Conversion time is defined as 0.104 milliseconds for 16 MHz Arduino by ADC_PRESCALE (=ADC_PRESCALE128) in ADCUtils.h. + * @ param aNumberOfSamples If > 64 an overflow may occur. + */ +uint16_t readADCChannelMultiSamplesWithReference(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aNumberOfSamples) { + uint16_t tSumValue = 0; + ADMUX = aADCChannelNumber | (aReference << SHIFT_VALUE_FOR_REFERENCE); + + ADCSRB = 0; // Free running mode. Only active if ADATE is set to 1. + // ADSC-StartConversion ADATE-AutoTriggerEnable ADIF-Reset Interrupt Flag + ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIF) | ADC_PRESCALE); + + for (uint8_t i = 0; i < aNumberOfSamples; i++) { + /* + * wait for free running conversion to finish. + * Do not wait for ADSC here, since ADSC is only low for 1 ADC Clock cycle on free running conversion. + */ + loop_until_bit_is_set(ADCSRA, ADIF); + + ADCSRA |= _BV(ADIF); // clear bit to enable recognizing next conversion has finished + // Add value + tSumValue += ADCL | (ADCH << 8); // using WordUnionForADCUtils does not save space here + // tSumValue += (ADCH << 8) | ADCL; // this does NOT work! + } + ADCSRA &= ~_BV(ADATE); // Disable auto-triggering (free running mode) + return tSumValue; +} + +/* + * Returns sum of all sample values + * Conversion time is defined as 0.104 milliseconds for 16 MHz Arduino for ADC_PRESCALE128 in ADCUtils.h. + * @ param aPrescale can be one of ADC_PRESCALE2, ADC_PRESCALE4, 8, 16, 32, 64, 128. + * ADC_PRESCALE32 is recommended for excellent linearity and fast readout of 26 microseconds + * @ param aNumberOfSamples If > 16k an overflow may occur. + */ +uint32_t readADCChannelMultiSamplesWithReferenceAndPrescaler(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aPrescale, + uint16_t aNumberOfSamples) { + uint32_t tSumValue = 0; + ADMUX = aADCChannelNumber | (aReference << SHIFT_VALUE_FOR_REFERENCE); + + ADCSRB = 0; // Free running mode. Only active if ADATE is set to 1. + // ADSC-StartConversion ADATE-AutoTriggerEnable ADIF-Reset Interrupt Flag + ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIF) | aPrescale); + + for (uint16_t i = 0; i < aNumberOfSamples; i++) { + /* + * wait for free running conversion to finish. + * Do not wait for ADSC here, since ADSC is only low for 1 ADC Clock cycle on free running conversion. + */ + loop_until_bit_is_set(ADCSRA, ADIF); + + ADCSRA |= _BV(ADIF); // clear bit to enable recognizing next conversion has finished + // Add value + tSumValue += ADCL | (ADCH << 8); // using WordUnionForADCUtils does not save space here + // tSumValue += (ADCH << 8) | ADCL; // this does NOT work! + } + ADCSRA &= ~_BV(ADATE); // Disable auto-triggering (free running mode) + return tSumValue; +} + +/* + * Returns sum of all sample values + * Assumes, that channel and reference are still set to the right values + * @ param aNumberOfSamples If > 16k an overflow may occur. + */ +uint32_t readADCChannelMultiSamples(uint8_t aPrescale, uint16_t aNumberOfSamples) { + uint32_t tSumValue = 0; + + ADCSRB = 0; // Free running mode. Only active if ADATE is set to 1. + // ADSC-StartConversion ADATE-AutoTriggerEnable ADIF-Reset Interrupt Flag + ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIF) | aPrescale); + + for (uint16_t i = 0; i < aNumberOfSamples; i++) { + /* + * wait for free running conversion to finish. + * Do not wait for ADSC here, since ADSC is only low for 1 ADC Clock cycle on free running conversion. + */ + loop_until_bit_is_set(ADCSRA, ADIF); + + ADCSRA |= _BV(ADIF); // clear bit to enable recognizing next conversion has finished + // Add value + tSumValue += ADCL | (ADCH << 8); // using WordUnionForADCUtils does not save space here + // tSumValue += (ADCH << 8) | ADCL; // this does NOT work! + } + ADCSRA &= ~_BV(ADATE); // Disable auto-triggering (free running mode) + return tSumValue; +} +/* + * use ADC_PRESCALE32 which gives 26 us conversion time and good linearity + * @return the maximum value of aNumberOfSamples samples. + */ +uint16_t readADCChannelWithReferenceMax(uint8_t aADCChannelNumber, uint8_t aReference, uint16_t aNumberOfSamples) { + uint16_t tADCValue = 0; + uint16_t tMaximum = 0; + ADMUX = aADCChannelNumber | (aReference << SHIFT_VALUE_FOR_REFERENCE); + + ADCSRB = 0; // Free running mode. Only active if ADATE is set to 1. + // ADSC-StartConversion ADATE-AutoTriggerEnable ADIF-Reset Interrupt Flag + ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIF) | ADC_PRESCALE32); + + for (uint16_t i = 0; i < aNumberOfSamples; i++) { + /* + * wait for free running conversion to finish. + * Do not wait for ADSC here, since ADSC is only low for 1 ADC Clock cycle on free running conversion. + */ + loop_until_bit_is_set(ADCSRA, ADIF); + + ADCSRA |= _BV(ADIF); // clear bit to enable recognizing next conversion has finished + // check value + tADCValue = ADCL | (ADCH << 8); + if (tADCValue > tMaximum) { + tMaximum = tADCValue; + } + } + ADCSRA &= ~_BV(ADATE); // Disable auto-triggering (free running mode) + return tMaximum; +} + +/* + * use ADC_PRESCALE32 which gives 26 us conversion time and good linearity + * @return the maximum value during aMicrosecondsToAquire measurement. + */ +uint16_t readADCChannelWithReferenceMaxMicros(uint8_t aADCChannelNumber, uint8_t aReference, uint16_t aMicrosecondsToAquire) { + uint16_t tNumberOfSamples = aMicrosecondsToAquire / 26; + return readADCChannelWithReferenceMax(aADCChannelNumber, aReference, tNumberOfSamples); +} + +/* + * aMaxRetries = 255 -> try forever + * @return (tMax + tMin) / 2 + */ +uint16_t readUntil4ConsecutiveValuesAreEqual(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aDelay, + uint8_t aAllowedDifference, uint8_t aMaxRetries) { + int tValues[4]; // last value is in tValues[3] + int tMin; + int tMax; + + /* + * Initialize first 4 values before checking + */ + tValues[0] = readADCChannelWithReference(aADCChannelNumber, aReference); + for (int i = 1; i < 4; ++i) { + if (aDelay != 0) { + delay(aDelay); // Minimum is only 3 delays! + } + tValues[i] = readADCChannelWithReference(aADCChannelNumber, aReference); + } + + do { + /* + * Get min and max of the last 4 values + */ + tMin = READING_FOR_AREF; + tMax = 0; + for (uint_fast8_t i = 0; i < 4; ++i) { + if (tValues[i] < tMin) { + tMin = tValues[i]; + } + if (tValues[i] > tMax) { + tMax = tValues[i]; + } + } + /* + * check for terminating condition + */ + if ((tMax - tMin) <= aAllowedDifference) { + break; + } else { + /* + * Get next value + */ +// Serial.print("Difference="); +// Serial.println(tMax - tMin); + // Move values to front + for (int i = 0; i < 3; ++i) { + tValues[i] = tValues[i + 1]; + } + // and wait before getting next value + if (aDelay != 0) { + delay(aDelay); + } + tValues[3] = readADCChannelWithReference(aADCChannelNumber, aReference); + } + if (aMaxRetries != 255) { + aMaxRetries--; + } + } while (aMaxRetries > 0); + +#if defined(LOCAL_DEBUG) + if(aMaxRetries == 0) { + Serial.print(F("No 4 equal values for difference ")); + Serial.print(aAllowedDifference); + Serial.print(F(" found ")); + Serial.print(tValues[0]); + Serial.print(' '); + Serial.print(tValues[1]); + Serial.print(' '); + Serial.print(tValues[2]); + Serial.print(' '); + Serial.println(tValues[3]); + } else { + Serial.print(aMaxRetries); + Serial.println(F(" retries left")); + } +#endif + + return (tMax + tMin) / 2; +} + +/* + * !!! Function without handling of switched reference and channel.!!! + * Use it ONLY if you only call getVCCVoltageSimple() or getVCCVoltageMillivoltSimple() in your program. + * !!! Resolution is only 20 millivolt !!! + * Raw reading of 1.1 V is 225 at 5 V. + * Raw reading of 1.1 V is 221 at 5.1 V. + * Raw reading of 1.1 V is 214 at 5.25 V (+5 %). + * Raw reading of 1.1 V is 204 at 5.5 V (+10 %). + */ +float getVCCVoltageSimple(void) { + // use AVCC with (optional) external capacitor at AREF pin as reference + float tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4); + return ((READING_FOR_AREF * 1.1 * 4) / tVCC); +} + +/* + * !!! Function without handling of switched reference and channel.!!! + * Use it ONLY if you only call getVCCVoltageSimple() or getVCCVoltageMillivoltSimple() in your program. + * !!! Resolution is only 20 millivolt !!! + */ +uint16_t getVCCVoltageMillivoltSimple(void) { + // use AVCC with external capacitor at AREF pin as reference + uint16_t tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4); + return ((READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCC); +} + +/* + * Gets the hypothetical 14 bit reading of VCC using 1.1 volt reference + * Similar to getVCCVoltageMillivolt() * 1024 / 1100 + */ +uint16_t getVCCVoltageReadingFor1_1VoltReference(void) { + uint16_t tVCC = waitAndReadADCChannelWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT); + /* + * Do not switch back ADMUX to enable checkAndWaitForReferenceAndChannelToSwitch() to work correctly for the next measurement + */ + return ((READING_FOR_AREF * READING_FOR_AREF) / tVCC); +} + +/* + * !!! Resolution is only 20 millivolt !!! + */ +float getVCCVoltage(void) { + return (getVCCVoltageMillivolt() / 1000.0); +} + +/* + * Read value of 1.1 volt internal channel using VCC (DEFAULT) as reference. + * Handles reference and channel switching by introducing the appropriate delays. + * !!! Resolution is only 20 millivolt !!! + * Raw reading of 1.1 V is 225 at 5 V. + * Raw reading of 1.1 V is 221 at 5.1 V. + * Raw reading of 1.1 V is 214 at 5.25 V (+5 %). + * Raw reading of 1.1 V is 204 at 5.5 V (+10 %). + */ +uint16_t getVCCVoltageMillivolt(void) { + uint16_t tVCC = waitAndReadADCChannelWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT); + /* + * Do not switch back ADMUX to enable checkAndWaitForReferenceAndChannelToSwitch() to work correctly for the next measurement + */ + return ((READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCC); +} + +/* + * Does not set sVCCVoltageMillivolt + */ +uint16_t printVCCVoltageMillivolt(Print *aSerial) { + aSerial->print(F("VCC=")); + uint16_t tVCCVoltageMillivolt = getVCCVoltageMillivolt(); + aSerial->print(tVCCVoltageMillivolt); + aSerial->println(" mV"); + return tVCCVoltageMillivolt; +} + +void readAndPrintVCCVoltageMillivolt(Print *aSerial) { + aSerial->print(F("VCC=")); + sVCCVoltageMillivolt = getVCCVoltageMillivolt(); + aSerial->print(sVCCVoltageMillivolt); + aSerial->println(" mV"); +} +/* + * !!! Function without handling of switched reference and channel.!!! + * Use it ONLY if you only call getVCCVoltageSimple() or getVCCVoltageMillivoltSimple() in your program. + * !!! Resolution is only 20 millivolt !!! + */ +void readVCCVoltageSimple(void) { + // use AVCC with (optional) external capacitor at AREF pin as reference + float tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4); + sVCCVoltage = (READING_FOR_AREF * (((float) ADC_INTERNAL_REFERENCE_MILLIVOLT) / 1000) * 4) / tVCC; +} + +/* + * !!! Function without handling of switched reference and channel.!!! + * Use it ONLY if you only call getVCCVoltageSimple() or getVCCVoltageMillivoltSimple() in your program. + * !!! Resolution is only 20 millivolt !!! + */ +void readVCCVoltageMillivoltSimple(void) { + // use AVCC with external capacitor at AREF pin as reference + uint16_t tVCCVoltageMillivoltRaw = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4); + sVCCVoltageMillivolt = (READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCCVoltageMillivoltRaw; +} + +/* + * !!! Resolution is only 20 millivolt !!! + */ +void readVCCVoltage(void) { + sVCCVoltage = getVCCVoltageMillivolt() / 1000.0; +} + +/* + * Read value of 1.1 volt internal channel using VCC (DEFAULT) as reference. + * Handles reference and channel switching by introducing the appropriate delays. + * !!! Resolution is only 20 millivolt !!! + * Sets also the sVCCVoltageMillivolt variable. + */ +void readVCCVoltageMillivolt(void) { + uint16_t tVCCVoltageMillivoltRaw = waitAndReadADCChannelWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT); + /* + * Do not switch back ADMUX to enable checkAndWaitForReferenceAndChannelToSwitch() to work correctly for the next measurement + */ + sVCCVoltageMillivolt = (READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCCVoltageMillivoltRaw; +} + +/* + * Get voltage at ADC channel aADCChannelForVoltageMeasurement + * aVCCVoltageMillivolt is assumed as reference voltage + */ +uint16_t getVoltageMillivolt(uint16_t aVCCVoltageMillivolt, uint8_t aADCChannelForVoltageMeasurement) { + uint16_t tInputVoltageRaw = waitAndReadADCChannelWithReference(aADCChannelForVoltageMeasurement, DEFAULT); + return (aVCCVoltageMillivolt * (uint32_t) tInputVoltageRaw) / READING_FOR_AREF; +} + +/* + * Get voltage at ADC channel aADCChannelForVoltageMeasurement + * Reference voltage VCC is determined just before + */ +uint16_t getVoltageMillivolt(uint8_t aADCChannelForVoltageMeasurement) { + uint16_t tInputVoltageRaw = waitAndReadADCChannelWithReference(aADCChannelForVoltageMeasurement, DEFAULT); + return (getVCCVoltageMillivolt() * (uint32_t) tInputVoltageRaw) / READING_FOR_AREF; +} + +uint16_t getVoltageMillivoltWith_1_1VoltReference(uint8_t aADCChannelForVoltageMeasurement) { + uint16_t tInputVoltageRaw = waitAndReadADCChannelWithReference(aADCChannelForVoltageMeasurement, INTERNAL); + return (ADC_INTERNAL_REFERENCE_MILLIVOLT * (uint32_t) tInputVoltageRaw) / READING_FOR_AREF; +} + +/* + * Return true if sVCCVoltageMillivolt is > 4.3 V and < 4.95 V + * This does not really work for the UNO board, because it has no series Diode in the USB VCC + * and therefore a very low voltage drop. + */ +bool isVCCUSBPowered() { + readVCCVoltageMillivolt(); + return (VOLTAGE_USB_POWERED_LOWER_THRESHOLD_MILLIVOLT < sVCCVoltageMillivolt + && sVCCVoltageMillivolt < VOLTAGE_USB_POWERED_UPPER_THRESHOLD_MILLIVOLT); +} + +/* + * Return true if sVCCVoltageMillivolt is > 4.3 V and < 4.95 V + */ +bool isVCCUSBPowered(Print *aSerial) { + readVCCVoltageMillivolt(); + aSerial->print(F("USB powered is ")); + bool tReturnValue; + if (VOLTAGE_USB_POWERED_LOWER_THRESHOLD_MILLIVOLT + < sVCCVoltageMillivolt&& sVCCVoltageMillivolt < VOLTAGE_USB_POWERED_UPPER_THRESHOLD_MILLIVOLT) { + tReturnValue = true; + aSerial->print(F("true ")); + } else { + tReturnValue = false; + aSerial->print(F("false ")); + } + printVCCVoltageMillivolt(aSerial); + return tReturnValue; +} + +/* + * @ return true only once, when VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP (6) times voltage too low -> shutdown + */ +bool isVCCUndervoltageMultipleTimes() { + /* + * Check VCC every VCC_CHECK_PERIOD_MILLIS (10) seconds + */ + + if (millis() - sLastVCCCheckMillis >= VCC_CHECK_PERIOD_MILLIS) { + sLastVCCCheckMillis = millis(); + +# if defined(INFO) + readAndPrintVCCVoltageMillivolt(&Serial); +# else + readVCCVoltageMillivolt(); +# endif + + if (sVCCTooLowCounter < VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP) { + /* + * Do not check again if shutdown has happened + */ + if (sVCCVoltageMillivolt > VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) { + sVCCTooLowCounter = 0; // reset counter + } else { + /* + * Voltage too low, wait VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP (6) times and then shut down. + */ + if (sVCCVoltageMillivolt < VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) { + // emergency shutdown + sVCCTooLowCounter = VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP; +# if defined(INFO) + Serial.println( + F( + "Voltage < " STR(VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) " mV detected -> emergency shutdown")); +# endif + } else { + sVCCTooLowCounter++; +# if defined(INFO) + Serial.print(F("Voltage < " STR(VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) " mV detected: ")); + Serial.print(VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP - sVCCTooLowCounter); + Serial.println(F(" tries left")); +# endif + } + if (sVCCTooLowCounter == VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP) { + /* + * 6 times voltage too low -> return signal for shutdown etc. + */ + return true; + } + } + } + } + return false; +} + +/* + * Return true if VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT (3 V) reached + */ +bool isVCCUndervoltage() { + readVCCVoltageMillivolt(); + return (sVCCVoltageMillivolt < VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT); +} + +/* + * Return true if VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT (3 V) reached + */ +bool isVCCEmergencyUndervoltage() { + readVCCVoltageMillivolt(); + return (sVCCVoltageMillivolt < VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT); +} + +void resetCounterForVCCUndervoltageMultipleTimes() { + sVCCTooLowCounter = 0; +} + +/* + * Recommended VCC is 1.8 V to 5.5 V, absolute maximum VCC is 6.0 V. + * Check for 5.25 V, because such overvoltage is quite unlikely to happen during regular operation. + * Raw reading of 1.1 V is 225 at 5 V. + * Raw reading of 1.1 V is 221 at 5.1 V. + * Raw reading of 1.1 V is 214 at 5.25 V (+5 %). + * Raw reading of 1.1 V is 204 at 5.5 V (+10 %). + * Raw reading of 1.1 V is 1126000 / VCC_MILLIVOLT + * @return true if 5 % overvoltage reached + */ +bool isVCCOvervoltage() { + readVCCVoltageMillivolt(); + return (sVCCVoltageMillivolt > VCC_OVERVOLTAGE_THRESHOLD_MILLIVOLT); +} +bool isVCCOvervoltageSimple() { + readVCCVoltageMillivoltSimple(); + return (sVCCVoltageMillivolt > VCC_OVERVOLTAGE_THRESHOLD_MILLIVOLT); +} + +// Version not using readVCCVoltageMillivoltSimple() +bool isVCCTooHighSimple() { + ADMUX = ADC_1_1_VOLT_CHANNEL_MUX | (DEFAULT << SHIFT_VALUE_FOR_REFERENCE); +// ADCSRB = 0; // Only active if ADATE is set to 1. +// ADSC-StartConversion ADIF-Reset Interrupt Flag - NOT free running mode + ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADIF) | ADC_PRESCALE128); // 128 -> 104 microseconds per ADC conversion at 16 MHz --- Arduino default +// wait for single conversion to finish + loop_until_bit_is_clear(ADCSRA, ADSC); + +// Get value + uint16_t tRawValue = ADCL | (ADCH << 8); + + return tRawValue < 1126000 / VCC_OVERVOLTAGE_THRESHOLD_MILLIVOLT; +} + +/* + * Temperature sensor is enabled by selecting the appropriate channel. + * Different formula for 328P and 328PB! + * !!! Function without handling of switched reference and channel.!!! + * Use it ONLY if you only use INTERNAL reference (e.g. only call getTemperatureSimple()) in your program. + */ +float getCPUTemperatureSimple(void) { +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + return 0.0; +#else + // use internal 1.1 volt as reference. 4 times oversample. Assume the signal has noise, but never verified :-( + uint16_t tTemperatureRaw = readADCChannelWithReferenceOversample(ADC_TEMPERATURE_CHANNEL_MUX, INTERNAL, 2); +#if defined(LOCAL_DEBUG) + Serial.print(F("TempRaw=")); + Serial.println(tTemperatureRaw); +#endif + +#if defined(__AVR_ATmega328PB__) + tTemperatureRaw -= 245; + return (float)tTemperatureRaw; +#elif defined(__AVR_ATtiny85__) + tTemperatureRaw -= 273; // 273 and 1.1666 are values from the datasheet + return (float)tTemperatureRaw / 1.1666; +#else + tTemperatureRaw -= 317; + return (float) tTemperatureRaw / 1.22; +#endif +#endif +} + +/* + * Handles usage of 1.1 V reference and channel switching by introducing the appropriate delays. + */ +float getTemperature(void) { + return getCPUTemperature(); +} +float getCPUTemperature(void) { +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + return 0.0; +#else + // use internal 1.1 volt as reference + checkAndWaitForReferenceAndChannelToSwitch(ADC_TEMPERATURE_CHANNEL_MUX, INTERNAL); + return getCPUTemperatureSimple(); +#endif +} + +#else // defined(ADC_UTILS_ARE_AVAILABLE) +// Dummy definition of functions defined in ADCUtils to compile examples for non AVR platforms without errors +/* + * Persistent storage for VCC value + */ +float sVCCVoltage; +uint16_t sVCCVoltageMillivolt; + +uint16_t getVCCVoltageMillivoltSimple(void){ + return 3300; +} + +uint16_t readADCChannelWithReferenceOversample(uint8_t aChannelNumber __attribute__((unused)), + uint8_t aReference __attribute__((unused)), uint8_t aOversampleExponent __attribute__((unused))) { + return 0; +} +float getCPUTemperature() { + return 20.0; +} +float getVCCVoltage() { + return 3.3; +} +#endif // defined(ADC_UTILS_ARE_AVAILABLE) + +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _ADC_UTILS_HPP diff --git a/examples/AllProtocolsOnLCD/AllProtocolsOnLCD.ino b/examples/AllProtocolsOnLCD/AllProtocolsOnLCD.ino new file mode 100644 index 000000000..36c5648bb --- /dev/null +++ b/examples/AllProtocolsOnLCD/AllProtocolsOnLCD.ino @@ -0,0 +1,480 @@ +/* + * AllProtocolsOnLCD.cpp + * + * Modified ReceiveDemo.cpp with additional 1602 LCD output. + * If debug button is pressed (pin connected to ground) a long output is generated. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2022-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. + +#if !defined(RAW_BUFFER_LENGTH) +// For air condition remotes it may require up to 750. Default is 200. +# if (defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF) +#define RAW_BUFFER_LENGTH 360 +# else +#define RAW_BUFFER_LENGTH 750 +# endif + +# if (defined(RAMEND) && RAMEND <= 0x8FF) || (defined(RAMSIZE) && RAMSIZE < 0x8FF) +#define DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE 200 // The decoder accepts mark or space durations up to 200 * 50 (MICROS_PER_TICK) = 10 milliseconds +# else +#define DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE 400 // The decoder accepts mark or space durations up to 400 * 50 (MICROS_PER_TICK) = 20 milliseconds +# endif +#endif + +//#define NO_LED_FEEDBACK_CODE // saves 92 bytes program memory +#if FLASHEND <= 0x1FFF // For 8k flash or less, like ATtiny85. Exclude exotic protocols. +#define EXCLUDE_EXOTIC_PROTOCOLS +# if !defined(DIGISTUMPCORE) // ATTinyCore is bigger than Digispark core +#define EXCLUDE_UNIVERSAL_PROTOCOLS // Saves up to 1000 bytes program memory. +# endif +#endif +//#define EXCLUDE_UNIVERSAL_PROTOCOLS // Saves up to 1000 bytes program memory. +//#define EXCLUDE_EXOTIC_PROTOCOLS // saves around 650 bytes program memory if all other protocols are active + +// MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding, +// to compensate for the signal forming of different IR receiver modules. See also IRremote.hpp line 142. +#define MARK_EXCESS_MICROS 20 // Adapt it to your IR receiver module. 20 is recommended for the cheap VS1838 modules. + +//#define RECORD_GAP_MICROS 12000 // Default is 8000. Activate it for some LG air conditioner protocols. + +//#define DEBUG // Activate this for lots of lovely debug output from the decoders. +//#define DECODE_NEC // Includes Apple and Onkyo + +#include + +/* + * Activate the type of LCD you use + * Default is parallel LCD with 2 rows of 16 characters (1602). + * Serial LCD has the disadvantage, that the first repeat is not detected, + * because of the long lasting serial communication. + */ +//#define USE_NO_LCD +//#define USE_SERIAL_LCD +// Definitions for the 1602 LCD +#define LCD_COLUMNS 16 +#define LCD_ROWS 2 + +#if defined(USE_SERIAL_LCD) +#include "LiquidCrystal_I2C.h" // Use an up to date library version, which has the init method +LiquidCrystal_I2C myLCD(0x27, LCD_COLUMNS, LCD_ROWS); // set the LCD address to 0x27 for a 16 chars and 2 line display +#elif !defined(USE_NO_LCD) +#define USE_PARALLEL_LCD +#include "LiquidCrystal.h" +//LiquidCrystal myLCD(4, 5, 6, 7, 8, 9); +LiquidCrystal myLCD(7, 8, 3, 4, 5, 6); +#endif + +#if defined(USE_PARALLEL_LCD) +#define DEBUG_BUTTON_PIN 11 // If low, print timing for each received data set +#undef TONE_PIN +#define TONE_PIN 9 // Pin 4 is used by LCD +#else +#define DEBUG_BUTTON_PIN 6 +#endif +#if defined(__AVR_ATmega328P__) +#define AUXILIARY_DEBUG_BUTTON_PIN 12 // Is set to low to enable using of a simple connector for enabling debug with pin 11 +#endif + +#define MILLIS_BETWEEN_ATTENTION_BEEP 60000 // 60 sec +uint32_t sMillisOfLastReceivedIRFrame = 0; + +#if defined(USE_SERIAL_LCD) || defined(USE_PARALLEL_LCD) +#define USE_LCD +# if defined(__AVR__) && defined(ADCSRA) && defined(ADATE) +// For cyclically display of VCC and isVCCUSBPowered() +#define VOLTAGE_USB_POWERED_LOWER_THRESHOLD_MILLIVOLT 4250 +#include "ADCUtils.hpp" +#define MILLIS_BETWEEN_VOLTAGE_PRINT 5000 +#define LCD_VOLTAGE_START_INDEX 11 +uint32_t volatile sMillisOfLastVoltagePrint = 0; +bool ProtocolStringOverwritesVoltage = false; +# endif +#define LCD_IR_COMMAND_START_INDEX 9 + +void printsVCCVoltageMillivoltOnLCD(); +void printIRResultOnLCD(); +size_t printByteHexOnLCD(uint16_t aHexByteValue); +void printSpacesOnLCD(uint_fast8_t aNumberOfSpacesToPrint); + +#endif // defined(USE_SERIAL_LCD) || defined(USE_PARALLEL_LCD) + +void setup() { +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604. Code does not fit in program memory of ATtiny85 etc. + pinMode(DEBUG_BUTTON_PIN, INPUT_PULLUP); +# if defined(AUXILIARY_DEBUG_BUTTON_PIN) + pinMode(AUXILIARY_DEBUG_BUTTON_PIN, OUTPUT); + digitalWrite(AUXILIARY_DEBUG_BUTTON_PIN, LOW); // To use a simple connector to enable debug +# endif +#endif + + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif +// Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + tone(TONE_PIN, 2200); + delay(200); + noTone(TONE_PIN); + +// In case the interrupt driver crashes on setup, give a clue +// to the user what's going on. + Serial.println(F("Enabling IRin...")); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); + +#if defined(USE_SERIAL_LCD) + Serial.println( + F("With serial LCD connection, the first repeat is not detected, because of the long lasting serial communication!")); +#endif + +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604. Code does not fit in program memory of ATtiny85 etc. + Serial.println(); + Serial.print(F("If you connect debug pin ")); +# if defined(APPLICATION_PIN_STRING) + Serial.print(APPLICATION_PIN_STRING); +# else + Serial.print(DEBUG_BUTTON_PIN); +# endif + Serial.print(F(" to ground")); +# if defined(AUXILIARY_DEBUG_BUTTON_PIN) + Serial.print(F(" or to pin ")); + Serial.print(AUXILIARY_DEBUG_BUTTON_PIN); +#endif + Serial.println(F(", raw data is always printed")); + + // Info for receive + Serial.print(RECORD_GAP_MICROS); + Serial.println(F(" us is the (minimum) gap, after which the start of a new IR packet is assumed")); + Serial.print(MARK_EXCESS_MICROS); + Serial.println(F(" us are subtracted from all marks and added to all spaces for decoding")); +#endif + +#if defined(USE_LCD) && defined(ADC_UTILS_ARE_AVAILABLE) + readVCCVoltageMillivolt(); +#endif + +#if defined(USE_SERIAL_LCD) + myLCD.init(); + myLCD.clear(); + myLCD.backlight(); // Switch backlight LED on +#endif +#if defined(USE_PARALLEL_LCD) + myLCD.begin(LCD_COLUMNS, LCD_ROWS); // This also clears display +#endif + +#if defined(USE_LCD) + myLCD.setCursor(0, 0); + myLCD.print(F("IRRemote v" VERSION_IRREMOTE)); + myLCD.setCursor(0, 1); + myLCD.print(F(__DATE__)); +#endif + +#if defined(USE_LCD) && defined(ADC_UTILS_ARE_AVAILABLE) + readVCCVoltageMillivolt(); +#endif +} + +void loop() { + /* + * Check if received data is available and if yes, try to decode it. + * Decoded result is in the IrReceiver.decodedIRData structure. + * + * E.g. command is in IrReceiver.decodedIRData.command + * address is in command is in IrReceiver.decodedIRData.address + * and up to 32 bit raw data in IrReceiver.decodedIRData.decodedRawData + */ + if (IrReceiver.decode()) { + Serial.println(); + // Print a short summary of received data + IrReceiver.printIRResultShort(&Serial); + + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) { + Serial.println(F("Try to increase the \"RAW_BUFFER_LENGTH\" value of " STR(RAW_BUFFER_LENGTH) " in " __FILE__)); +#if defined(USE_LCD) + myLCD.setCursor(0, 0); + myLCD.print(F("Overflow ")); +#endif + + // see also https://github.com/Arduino-IRremote/Arduino-IRremote#compile-options--macros-for-this-library + + } else { + // play tone + auto tStartMillis = millis(); +// IrReceiver.stopTimer(); // Not really required for Uno, but we then should use restartTimer(aMicrosecondsToAddToGapCounter) + tone(TONE_PIN, 2200); + + if ((IrReceiver.decodedIRData.protocol == UNKNOWN || digitalRead(DEBUG_BUTTON_PIN) == LOW) +#if defined(USE_LCD) && defined(ADC_UTILS_ARE_AVAILABLE) + || isVCCUSBPowered() +#endif + ) { + // Print more info, but only if we are connected to USB, i.e. VCC is > 4300 mV, because this may take to long to detect some fast repeats + IrReceiver.printIRSendUsage(&Serial); +// IrReceiver.printIRResultRawFormatted(&Serial, false); // print ticks, this is faster :-) + IrReceiver.printIRResultRawFormatted(&Serial); // print us, this is better to compare :-) + } + + // Guarantee at least 5 millis for tone. decode starts 5 millis (RECORD_GAP_MICROS) after end of frame + // so here we are 10 millis after end of frame. Sony20 has only a 12 ms repeat gap. + while ((millis() - tStartMillis) < 5) + ; + noTone(TONE_PIN); + IrReceiver.restartTimer(5000); // Restart IR timer. + +#if defined(USE_LCD) + printIRResultOnLCD(); +#endif + } + + /* + * !!!Important!!! Enable receiving of the next value, + * since receiving has stopped after the end of the current received data packet. + */ + IrReceiver.resume(); + } // if (IrReceiver.decode()) + + /* + * Check if generating attention beep every minute, after the current measurement was finished + */ + if ((millis() - sMillisOfLastReceivedIRFrame) >= MILLIS_BETWEEN_ATTENTION_BEEP +#if defined(USE_LCD) && defined(ADC_UTILS_ARE_AVAILABLE) + && !isVCCUSBPowered() +#endif + ) { + sMillisOfLastReceivedIRFrame = millis(); +#if defined(USE_LCD) && defined(ADC_UTILS_ARE_AVAILABLE) + printsVCCVoltageMillivoltOnLCD(); +#endif +// IrReceiver.stopTimer(); // Not really required for Uno, but we then should use restartTimer(aMicrosecondsToAddToGapCounter) + tone(TONE_PIN, 2200); + delay(50); + noTone(TONE_PIN); + IrReceiver.restartTimer(50000); + } + +#if defined(USE_LCD) && defined(ADC_UTILS_ARE_AVAILABLE) + //Periodically print VCC + if (!ProtocolStringOverwritesVoltage && millis() - sMillisOfLastVoltagePrint > MILLIS_BETWEEN_VOLTAGE_PRINT) { + /* + * Periodically print VCC + */ + sMillisOfLastVoltagePrint = millis(); + readVCCVoltageMillivolt(); + printsVCCVoltageMillivoltOnLCD(); + } +#endif + +} + +#if defined(USE_LCD) +void printsVCCVoltageMillivoltOnLCD() { +# if defined(ADC_UTILS_ARE_AVAILABLE) + char tVoltageString[5]; + dtostrf(sVCCVoltageMillivolt / 1000.0, 4, 2, tVoltageString); + myLCD.setCursor(LCD_VOLTAGE_START_INDEX - 1, 0); + myLCD.print(' '); + myLCD.print(tVoltageString); + myLCD.print('V'); +# endif +} + +/* + * LCD output for 1602 LCDs + * 40 - 55 Milliseconds per initial output + * The expander runs at 100 kHz :-( + * 8 milliseconds for 8 bit; 10 ms for 16 bit code output + * 3 milliseconds for repeat output + * + */ +void printIRResultOnLCD() { + static uint16_t sLastProtocolIndex = 4711; + static uint16_t sLastProtocolAddress = 4711; + static uint16_t sLastCommand = 0; + static uint8_t sLastCommandPrintPosition; + + /* + * Print only if protocol has changed + */ + if (sLastProtocolIndex != IrReceiver.decodedIRData.protocol) { + sLastProtocolIndex = IrReceiver.decodedIRData.protocol; + /* + * Show protocol name and handle overwrite over voltage display + */ + myLCD.setCursor(0, 0); + uint_fast8_t tProtocolStringLength = myLCD.print(getProtocolString(IrReceiver.decodedIRData.protocol)); +# if defined(__AVR__) && defined(ADCSRA) && defined(ADATE) + if (tProtocolStringLength > LCD_VOLTAGE_START_INDEX) { + // we overwrite the voltage -> clear rest of line and inhibit new printing of voltage + ProtocolStringOverwritesVoltage = true; + if (tProtocolStringLength < LCD_COLUMNS) { + printSpacesOnLCD(LCD_COLUMNS - tProtocolStringLength); + } + } else { + // Trigger printing of VCC in main loop + sMillisOfLastVoltagePrint = 0; + ProtocolStringOverwritesVoltage = false; + printSpacesOnLCD(LCD_VOLTAGE_START_INDEX - tProtocolStringLength); + } +# else + printSpacesOnLCD(LCD_COLUMNS - tProtocolStringLength); +# endif + } + + if (IrReceiver.decodedIRData.protocol == UNKNOWN) { + /* + * Print number of bits received and hash code or microseconds of signal + */ + myLCD.setCursor(0, 1); + uint8_t tNumberOfBits = (IrReceiver.decodedIRData.rawDataPtr->rawlen + 1) / 2; + uint_fast8_t tPrintedStringLength = myLCD.print(tNumberOfBits); + myLCD.print(F(" bit ")); + + if (IrReceiver.decodedIRData.decodedRawData != 0) { + if (tNumberOfBits < 10) { + myLCD.print('0'); + tPrintedStringLength++; + } + myLCD.print('x'); + tPrintedStringLength += myLCD.print(IrReceiver.decodedIRData.decodedRawData, HEX) + 1; + } else { + tPrintedStringLength += myLCD.print(IrReceiver.getTotalDurationOfRawData()); + myLCD.print(F(" \xE4s")); // \xE4 is micro symbol + } + printSpacesOnLCD(11 - tPrintedStringLength); + sLastProtocolAddress = 4711; + sLastCommand = 44711; + + } else { + /* + * Protocol is know here + * Print address only if it has changed + */ + if (sLastProtocolAddress != IrReceiver.decodedIRData.address || IrReceiver.decodedIRData.protocol == PULSE_DISTANCE + || IrReceiver.decodedIRData.protocol == PULSE_WIDTH) { + + myLCD.setCursor(0, 1); + /* + * Show address + */ +# if defined(DECODE_DISTANCE_WIDTH) + if (IrReceiver.decodedIRData.protocol == PULSE_DISTANCE || IrReceiver.decodedIRData.protocol == PULSE_WIDTH) { + sLastProtocolAddress = 4711; // To enforce next print of address + myLCD.print(F("[0]=0x")); + uint_fast8_t tAddressStringLength = myLCD.print(IrReceiver.decodedIRData.decodedRawDataArray[0], HEX); + printSpacesOnLCD(LCD_COLUMNS - tAddressStringLength); + sLastCommand = 0; // to trigger restoration of "C=" string, if another protocol is received + /* + * No command here! + */ + return; + + } else { +# endif + sLastProtocolAddress = IrReceiver.decodedIRData.address; +// Serial.print(F("Print address 0x")); +// Serial.println(IrReceiver.decodedIRData.address, HEX); + myLCD.print(F("A=")); + uint_fast8_t tAddressStringLength = printByteHexOnLCD(IrReceiver.decodedIRData.address); + printSpacesOnLCD((LCD_IR_COMMAND_START_INDEX - 2) - tAddressStringLength); +# if defined(DECODE_DISTANCE_WIDTH) + } +# endif + } + + /* + * Print command always + */ + uint16_t tCommand = IrReceiver.decodedIRData.command; + +// Check if prefix position must change + if (sLastCommand == 0 || (sLastCommand >= 0x100 && tCommand < 0x100) || (sLastCommand < 0x100 && tCommand >= 0x100)) { + sLastCommand = tCommand; + /* + * Print prefix for 8/16 bit commands + */ + if (tCommand >= 0x100) { + // Do not print "C=" here to have 2 additional characters for command + sLastCommandPrintPosition = 9; + } else { + myLCD.setCursor(LCD_IR_COMMAND_START_INDEX, 1); + myLCD.print(F("C=")); + sLastCommandPrintPosition = 11; + } + } + + /* + * Command data + */ +// Serial.print(F("Print command 0x")); +// Serial.print(tCommand, HEX); +// Serial.print(F(" at ")); +// Serial.println(sLastCommandPrintPosition); + myLCD.setCursor(sLastCommandPrintPosition, 1); + printByteHexOnLCD(tCommand); + + /* + * Show or clear repetition flag + */ + if (IrReceiver.decodedIRData.flags & (IRDATA_FLAGS_IS_REPEAT)) { + myLCD.print('R'); + return; // Since it is a repetition, printed data has not changed + } else { + myLCD.print(' '); + } + } // IrReceiver.decodedIRData.protocol == UNKNOWN +} + +size_t printByteHexOnLCD(uint16_t aHexByteValue) { + myLCD.print(F("0x")); + size_t tPrintSize = 2; + if (aHexByteValue < 0x10 || (aHexByteValue > 0x100 && aHexByteValue < 0x1000)) { + myLCD.print('0'); // leading 0 + tPrintSize++; + } + return myLCD.print(aHexByteValue, HEX) + tPrintSize; +} + +void printSpacesOnLCD(uint_fast8_t aNumberOfSpacesToPrint) { + for (uint_fast8_t i = 0; i < aNumberOfSpacesToPrint; ++i) { + myLCD.print(' '); + } +} +#endif // defined(USE_LCD) diff --git a/examples/AllProtocolsOnLCD/LiquidCrystal.cpp b/examples/AllProtocolsOnLCD/LiquidCrystal.cpp new file mode 100644 index 000000000..5dcef0838 --- /dev/null +++ b/examples/AllProtocolsOnLCD/LiquidCrystal.cpp @@ -0,0 +1,326 @@ +#include "LiquidCrystal.h" + +#include +#include +#include +#include "Arduino.h" + +// When the display powers up, it is configured as follows: +// +// 1. Display clear +// 2. Function set: +// DL = 1; 8-bit interface data +// N = 0; 1-line display +// F = 0; 5x8 dot character font +// 3. Display on/off control: +// D = 0; Display off +// C = 0; Cursor off +// B = 0; Blinking off +// 4. Entry mode set: +// I/D = 1; Increment by 1 +// S = 0; No shift +// +// Note, however, that resetting the Arduino doesn't reset the LCD, so we +// can't assume that its in that state when a sketch starts (and the +// LiquidCrystal constructor is called). + +LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) +{ + init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7); +} + +LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) +{ + init(0, rs, 255, enable, d0, d1, d2, d3, d4, d5, d6, d7); +} + +LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) +{ + init(1, rs, rw, enable, d0, d1, d2, d3, 0, 0, 0, 0); +} + +LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) +{ + init(1, rs, 255, enable, d0, d1, d2, d3, 0, 0, 0, 0); +} + +void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) +{ + _rs_pin = rs; + _rw_pin = rw; + _enable_pin = enable; + + _data_pins[0] = d0; + _data_pins[1] = d1; + _data_pins[2] = d2; + _data_pins[3] = d3; + _data_pins[4] = d4; + _data_pins[5] = d5; + _data_pins[6] = d6; + _data_pins[7] = d7; + + if (fourbitmode) + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + else + _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS; + + begin(16, 1); +} + +void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { + if (lines > 1) { + _displayfunction |= LCD_2LINE; + } + _numlines = lines; + + setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols); + + // for some 1 line displays you can select a 10 pixel high font + if ((dotsize != LCD_5x8DOTS) && (lines == 1)) { + _displayfunction |= LCD_5x10DOTS; + } + + pinMode(_rs_pin, OUTPUT); + // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin# + if (_rw_pin != 255) { + pinMode(_rw_pin, OUTPUT); + } + pinMode(_enable_pin, OUTPUT); + + // Do these once, instead of every time a character is drawn for speed reasons. + for (int i=0; i<((_displayfunction & LCD_8BITMODE) ? 8 : 4); ++i) + { + pinMode(_data_pins[i], OUTPUT); + } + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way before 4.5V so we'll wait 50 + delayMicroseconds(50000); + // Now we pull both RS and R/W low to begin commands + digitalWrite(_rs_pin, LOW); + digitalWrite(_enable_pin, LOW); + if (_rw_pin != 255) { + digitalWrite(_rw_pin, LOW); + } + + //put the LCD into 4 bit or 8 bit mode + if (! (_displayfunction & LCD_8BITMODE)) { + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + write4bits(0x03); + delayMicroseconds(4500); // wait min 4.1ms + + // second try + write4bits(0x03); + delayMicroseconds(4500); // wait min 4.1ms + + // third go! + write4bits(0x03); + delayMicroseconds(150); + + // finally, set to 4-bit interface + write4bits(0x02); + } else { + // this is according to the hitachi HD44780 datasheet + // page 45 figure 23 + + // Send function set command sequence + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(4500); // wait more than 4.1ms + + // second try + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(150); + + // third go + command(LCD_FUNCTIONSET | _displayfunction); + } + + // finally, set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear it off + clear(); + + // Initialize to default text direction (for romance languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); + +} + +void LiquidCrystal::setRowOffsets(int row0, int row1, int row2, int row3) +{ + _row_offsets[0] = row0; + _row_offsets[1] = row1; + _row_offsets[2] = row2; + _row_offsets[3] = row3; +} + +/********** high level commands, for the user! */ +void LiquidCrystal::clear() +{ + command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void LiquidCrystal::home() +{ + command(LCD_RETURNHOME); // set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void LiquidCrystal::setCursor(uint8_t col, uint8_t row) +{ + const size_t max_lines = sizeof(_row_offsets) / sizeof(*_row_offsets); + if ( row >= max_lines ) { + row = max_lines - 1; // we count rows starting w/0 + } + if ( row >= _numlines ) { + row = _numlines - 1; // we count rows starting w/0 + } + + command(LCD_SETDDRAMADDR | (col + _row_offsets[row])); +} + +// Turn the display on/off (quickly) +void LiquidCrystal::noDisplay() { + _displaycontrol &= ~LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal::display() { + _displaycontrol |= LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turns the underline cursor on/off +void LiquidCrystal::noCursor() { + _displaycontrol &= ~LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal::cursor() { + _displaycontrol |= LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turn on and off the blinking cursor +void LiquidCrystal::noBlink() { + _displaycontrol &= ~LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal::blink() { + _displaycontrol |= LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// These commands scroll the display without changing the RAM +void LiquidCrystal::scrollDisplayLeft(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); +} +void LiquidCrystal::scrollDisplayRight(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); +} + +// This is for text that flows Left to Right +void LiquidCrystal::leftToRight(void) { + _displaymode |= LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This is for text that flows Right to Left +void LiquidCrystal::rightToLeft(void) { + _displaymode &= ~LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'right justify' text from the cursor +void LiquidCrystal::autoscroll(void) { + _displaymode |= LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'left justify' text from the cursor +void LiquidCrystal::noAutoscroll(void) { + _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// Allows us to fill the first 8 CGRAM locations +// with custom characters +void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) { + location &= 0x7; // we only have 8 locations 0-7 + command(LCD_SETCGRAMADDR | (location << 3)); + for (int i=0; i<8; i++) { + write(charmap[i]); + } +} + +/*********** mid level commands, for sending data/cmds */ + +inline void LiquidCrystal::command(uint8_t value) { + send(value, LOW); +} + +inline size_t LiquidCrystal::write(uint8_t value) { + send(value, HIGH); + return 1; // assume sucess +} + +/************ low level data pushing commands **********/ + +// write either command or data, with automatic 4/8-bit selection +void LiquidCrystal::send(uint8_t value, uint8_t mode) { + digitalWrite(_rs_pin, mode); + + // if there is a RW pin indicated, set it low to Write + if (_rw_pin != 255) { + digitalWrite(_rw_pin, LOW); + } + + if (_displayfunction & LCD_8BITMODE) { + write8bits(value); + } else { + write4bits(value>>4); + write4bits(value); + } +} + +void LiquidCrystal::pulseEnable(void) { + digitalWrite(_enable_pin, LOW); + delayMicroseconds(1); + digitalWrite(_enable_pin, HIGH); + delayMicroseconds(1); // enable pulse must be >450ns + digitalWrite(_enable_pin, LOW); + delayMicroseconds(100); // commands need > 37us to settle +} + +void LiquidCrystal::write4bits(uint8_t value) { + for (int i = 0; i < 4; i++) { + digitalWrite(_data_pins[i], (value >> i) & 0x01); + } + + pulseEnable(); +} + +void LiquidCrystal::write8bits(uint8_t value) { + for (int i = 0; i < 8; i++) { + digitalWrite(_data_pins[i], (value >> i) & 0x01); + } + + pulseEnable(); +} diff --git a/examples/AllProtocolsOnLCD/LiquidCrystal.h b/examples/AllProtocolsOnLCD/LiquidCrystal.h new file mode 100644 index 000000000..072062425 --- /dev/null +++ b/examples/AllProtocolsOnLCD/LiquidCrystal.h @@ -0,0 +1,108 @@ +#ifndef LiquidCrystal_h +#define LiquidCrystal_h + +#include +#include "Print.h" + +// commands +#define LCD_CLEARDISPLAY 0x01 +#define LCD_RETURNHOME 0x02 +#define LCD_ENTRYMODESET 0x04 +#define LCD_DISPLAYCONTROL 0x08 +#define LCD_CURSORSHIFT 0x10 +#define LCD_FUNCTIONSET 0x20 +#define LCD_SETCGRAMADDR 0x40 +#define LCD_SETDDRAMADDR 0x80 + +// flags for display entry mode +#define LCD_ENTRYRIGHT 0x00 +#define LCD_ENTRYLEFT 0x02 +#define LCD_ENTRYSHIFTINCREMENT 0x01 +#define LCD_ENTRYSHIFTDECREMENT 0x00 + +// flags for display on/off control +#define LCD_DISPLAYON 0x04 +#define LCD_DISPLAYOFF 0x00 +#define LCD_CURSORON 0x02 +#define LCD_CURSOROFF 0x00 +#define LCD_BLINKON 0x01 +#define LCD_BLINKOFF 0x00 + +// flags for display/cursor shift +#define LCD_DISPLAYMOVE 0x08 +#define LCD_CURSORMOVE 0x00 +#define LCD_MOVERIGHT 0x04 +#define LCD_MOVELEFT 0x00 + +// flags for function set +#define LCD_8BITMODE 0x10 +#define LCD_4BITMODE 0x00 +#define LCD_2LINE 0x08 +#define LCD_1LINE 0x00 +#define LCD_5x10DOTS 0x04 +#define LCD_5x8DOTS 0x00 + +class LiquidCrystal : public Print { +public: + LiquidCrystal(uint8_t rs, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); + LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); + LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3); + LiquidCrystal(uint8_t rs, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3); + + void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable, + uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); + + void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS); + + void clear(); + void home(); + + void noDisplay(); + void display(); + void noBlink(); + void blink(); + void noCursor(); + void cursor(); + void scrollDisplayLeft(); + void scrollDisplayRight(); + void leftToRight(); + void rightToLeft(); + void autoscroll(); + void noAutoscroll(); + + void setRowOffsets(int row1, int row2, int row3, int row4); + void createChar(uint8_t, uint8_t[]); + void setCursor(uint8_t, uint8_t); + virtual size_t write(uint8_t); + void command(uint8_t); + + using Print::write; +private: + void send(uint8_t, uint8_t); + void write4bits(uint8_t); + void write8bits(uint8_t); + void pulseEnable(); + + uint8_t _rs_pin; // LOW: command. HIGH: character. + uint8_t _rw_pin; // LOW: write to LCD. HIGH: read from LCD. + uint8_t _enable_pin; // activated by a HIGH pulse. + uint8_t _data_pins[8]; + + uint8_t _displayfunction; + uint8_t _displaycontrol; + uint8_t _displaymode; + + uint8_t _initialized; + + uint8_t _numlines; + uint8_t _row_offsets[4]; +}; + +#endif diff --git a/examples/AllProtocolsOnLCD/LiquidCrystal_I2C.cpp b/examples/AllProtocolsOnLCD/LiquidCrystal_I2C.cpp new file mode 100644 index 000000000..7ac4cead0 --- /dev/null +++ b/examples/AllProtocolsOnLCD/LiquidCrystal_I2C.cpp @@ -0,0 +1,375 @@ +// LiquidCrystal_I2C.hpp +// Based on the work by DFRobot +/* + * Extensions made by AJ 2023 + * Removed Arduino 0.x support + * Added SoftI2CMaste support, which drastically reduces program size. + * Added OLED stuff + * Added createChar() with PROGMEM input + * Added fast timing + */ +#ifndef _LIQUID_CRYSTAL_I2C_HPP +#define _LIQUID_CRYSTAL_I2C_HPP + +#include "Arduino.h" + +#if defined(__AVR__) && !defined(USE_SOFT_I2C_MASTER) && __has_include("SoftI2CMasterConfig.h") +#define USE_SOFT_I2C_MASTER // must be before #include "LiquidCrystal_I2C.h" +#endif + +#include "LiquidCrystal_I2C.h" +#include + +inline size_t LiquidCrystal_I2C::write(uint8_t value) { + send(value, Rs); + return 1; +} + +#if defined(USE_SOFT_I2C_MASTER) +//#define USE_SOFT_I2C_MASTER_H_AS_PLAIN_INCLUDE +#include "SoftI2CMasterConfig.h" // Include configuration for sources +#include "SoftI2CMaster.h" // include sources +#elif defined(USE_SOFT_WIRE) +#define USE_SOFTWIRE_H_AS_PLAIN_INCLUDE +#include "SoftWire.h" +#endif + +#if defined(__AVR__) +/* + * The datasheet says: a command need > 37us to settle. Enable pulse must be > 450ns. + * Use no delay for enable pulse after each command, + * because the overhead of this library seems to be using the 37 us and 450 ns. + * At least it works perfectly for all my LCD's connected to Uno, Nano etc. + * and it saves a lot of time in realtime applications using LCD as display, + * like https://github.com/ArminJo/Arduino-DTSU666H_PowerMeter + */ +#define USE_FAST_TIMING +#endif + +// When the display powers up, it is configured as follows: +// +// 1. Display clear +// 2. Function set: +// DL = 1; 8-bit interface data +// N = 0; 1-line display +// F = 0; 5x8 dot character font +// 3. Display on/off control: +// D = 0; Display off +// C = 0; Cursor off +// B = 0; Blinking off +// 4. Entry mode set: +// I/D = 1; Increment by 1 +// S = 0; No shift +// +// Note, however, that resetting the Arduino doesn't reset the LCD, so we +// can't assume that its in that state when a sketch starts (and the +// LiquidCrystal constructor is called). + +LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t lcd_cols, uint8_t lcd_rows) { + _Addr = lcd_Addr; + _cols = lcd_cols; + _rows = lcd_rows; + _backlightval = LCD_NOBACKLIGHT; + _oled = false; +} + +void LiquidCrystal_I2C::oled_init() { + _oled = true; + init_priv(); +} + +void LiquidCrystal_I2C::init() { + init_priv(); +} + +void LiquidCrystal_I2C::init_priv() { +#if defined(USE_SOFT_I2C_MASTER) + i2c_init(); +#else + Wire.begin(); +#endif + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + begin(_cols, _rows); +} + +void LiquidCrystal_I2C::begin(uint8_t cols __attribute__((unused)), uint8_t lines, uint8_t dotsize) { + if (lines > 1) { + _displayfunction |= LCD_2LINE; + } + _numlines = lines; + + // for some 1 line displays you can select a 10 pixel high font + if ((dotsize != 0) && (lines == 1)) { + _displayfunction |= LCD_5x10DOTS; + } + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way before 4.5V so we'll wait 50 + delay(50); + + // Now we pull both RS and R/W low to begin commands + expanderWrite(_backlightval); // reset expander and turn backlight off (Bit 8 =1) + delay(1000); + + //put the LCD into 4 bit mode + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + write4bits(0x03 << 4); + delayMicroseconds(4500); // wait min 4.1ms + + // second try + write4bits(0x03 << 4); + delayMicroseconds(4500); // wait min 4.1ms + + // third go! + write4bits(0x03 << 4); + delayMicroseconds(150); + + // finally, set to 4-bit interface + write4bits(0x02 << 4); + + // set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear it off + clear(); + + // Initialize to default text direction (for roman languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); + + home(); + +} + +/********** high level commands, for the user! */ +void LiquidCrystal_I2C::clear() { + command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero +#if defined(USE_FAST_TIMING) + delayMicroseconds(1500); // this command takes a long time! // AJ 20.9.23 1200 is too short for my 2004 LCD's, 1400 is OK +#else + delayMicroseconds(2000); // this command takes a long time! +#endif + if (_oled) + setCursor(0, 0); +} + +void LiquidCrystal_I2C::home() { + command(LCD_RETURNHOME); // set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void LiquidCrystal_I2C::setCursor(uint8_t col, uint8_t row) { + int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; + if (row > _numlines) { + row = _numlines - 1; // we count rows starting w/0 + } + command(LCD_SETDDRAMADDR | (col + row_offsets[row])); +} + +// Turn the display on/off (quickly) +void LiquidCrystal_I2C::noDisplay() { + _displaycontrol &= ~LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal_I2C::display() { + _displaycontrol |= LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turns the underline cursor on/off +void LiquidCrystal_I2C::noCursor() { + _displaycontrol &= ~LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal_I2C::cursor() { + _displaycontrol |= LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turn on and off the blinking cursor +void LiquidCrystal_I2C::noBlink() { + _displaycontrol &= ~LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal_I2C::blink() { + _displaycontrol |= LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// These commands scroll the display without changing the RAM +void LiquidCrystal_I2C::scrollDisplayLeft(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); +} +void LiquidCrystal_I2C::scrollDisplayRight(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); +} + +// This is for text that flows Left to Right +void LiquidCrystal_I2C::leftToRight(void) { + _displaymode |= LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This is for text that flows Right to Left +void LiquidCrystal_I2C::rightToLeft(void) { + _displaymode &= ~LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'right justify' text from the cursor +void LiquidCrystal_I2C::autoscroll(void) { + _displaymode |= LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'left justify' text from the cursor +void LiquidCrystal_I2C::noAutoscroll(void) { + _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// Allows us to fill the first 8 CGRAM locations +// with custom characters +void LiquidCrystal_I2C::createChar(uint8_t location, uint8_t charmap[]) { + location &= 0x7; // we only have 8 locations 0-7 + command(LCD_SETCGRAMADDR | (location << 3)); + for (int i = 0; i < 8; i++) { + write(charmap[i]); + } +} + +//createChar with PROGMEM input +void LiquidCrystal_I2C::createChar(uint8_t location, const char *charmap) { + location &= 0x7; // we only have 8 locations 0-7 + command(LCD_SETCGRAMADDR | (location << 3)); + for (int i = 0; i < 8; i++) { + write(pgm_read_byte_near(charmap++)); + } +} + +// Turn the (optional) backlight off/on +void LiquidCrystal_I2C::noBacklight(void) { + _backlightval = LCD_NOBACKLIGHT; + expanderWrite(0); +} + +void LiquidCrystal_I2C::backlight(void) { + _backlightval = LCD_BACKLIGHT; + expanderWrite(0); +} + +/*********** mid level commands, for sending data/cmds */ + +inline void LiquidCrystal_I2C::command(uint8_t value) { + send(value, 0); +} + +/************ low level data pushing commands **********/ + +// write either command or data +void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) { + uint8_t highnib = value & 0xf0; + uint8_t lownib = (value << 4) & 0xf0; + write4bits((highnib) | mode); + write4bits((lownib) | mode); +} + +void LiquidCrystal_I2C::write4bits(uint8_t value) { + expanderWrite(value); + pulseEnable(value); +} + +void LiquidCrystal_I2C::expanderWrite(uint8_t _data) { +#if defined(USE_SOFT_I2C_MASTER) + i2c_write_byte(_Addr << 1, _data | _backlightval); +#else + Wire.beginTransmission(_Addr); + Wire.write((int )(_data) | _backlightval); + Wire.endTransmission(); +#endif +} + +void LiquidCrystal_I2C::pulseEnable(uint8_t _data) { + expanderWrite(_data | En); // En high +#if !defined(USE_FAST_TIMING) + delayMicroseconds(1); // enable pulse must be > 450ns // AJ 20.9.23 not required for my LCD's +#endif + expanderWrite(_data & ~En); // En low +#if !defined(USE_FAST_TIMING) + delayMicroseconds(50); // commands need > 37us to settle // AJ 20.9.23 not required for my LCD's +#endif +} + +// Alias functions + +void LiquidCrystal_I2C::cursor_on() { + cursor(); +} + +void LiquidCrystal_I2C::cursor_off() { + noCursor(); +} + +void LiquidCrystal_I2C::blink_on() { + blink(); +} + +void LiquidCrystal_I2C::blink_off() { + noBlink(); +} + +void LiquidCrystal_I2C::load_custom_character(uint8_t char_num, uint8_t *rows) { + createChar(char_num, rows); +} + +void LiquidCrystal_I2C::setBacklight(uint8_t new_val) { + if (new_val) { + backlight(); // turn backlight on + } else { + noBacklight(); // turn backlight off + } +} + +void LiquidCrystal_I2C::printstr(const char c[]) { + //This function is not identical to the function used for "real" I2C displays + //it's here so the user sketch doesn't have to be changed + print(c); +} + +// unsupported API functions +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +void LiquidCrystal_I2C::off() { +} +void LiquidCrystal_I2C::on() { +} +void LiquidCrystal_I2C::setDelay(int cmdDelay, int charDelay) { +} +uint8_t LiquidCrystal_I2C::status() { + return 0; +} +uint8_t LiquidCrystal_I2C::keypad() { + return 0; +} +uint8_t LiquidCrystal_I2C::init_bargraph(uint8_t graphtype) { + return 0; +} +void LiquidCrystal_I2C::draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end) { +} +void LiquidCrystal_I2C::draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_row_end) { +} +void LiquidCrystal_I2C::setContrast(uint8_t new_val) { +} +#pragma GCC diagnostic pop + +#endif // _LIQUID_CRYSTAL_I2C_HPP diff --git a/examples/AllProtocolsOnLCD/LiquidCrystal_I2C.h b/examples/AllProtocolsOnLCD/LiquidCrystal_I2C.h new file mode 100644 index 000000000..420214a48 --- /dev/null +++ b/examples/AllProtocolsOnLCD/LiquidCrystal_I2C.h @@ -0,0 +1,129 @@ +//YWROBOT +#ifndef LiquidCrystal_I2C_h +#define LiquidCrystal_I2C_h + +#include +#include "Print.h" +#if !defined(USE_SOFT_I2C_MASTER) && !defined(USE_SOFT_WIRE) +#include +#endif + +// commands +#define LCD_CLEARDISPLAY 0x01 +#define LCD_RETURNHOME 0x02 +#define LCD_ENTRYMODESET 0x04 +#define LCD_DISPLAYCONTROL 0x08 +#define LCD_CURSORSHIFT 0x10 +#define LCD_FUNCTIONSET 0x20 +#define LCD_SETCGRAMADDR 0x40 +#define LCD_SETDDRAMADDR 0x80 + +// flags for display entry mode +#define LCD_ENTRYRIGHT 0x00 +#define LCD_ENTRYLEFT 0x02 +#define LCD_ENTRYSHIFTINCREMENT 0x01 +#define LCD_ENTRYSHIFTDECREMENT 0x00 + +// flags for display on/off control +#define LCD_DISPLAYON 0x04 +#define LCD_DISPLAYOFF 0x00 +#define LCD_CURSORON 0x02 +#define LCD_CURSOROFF 0x00 +#define LCD_BLINKON 0x01 +#define LCD_BLINKOFF 0x00 + +// flags for display/cursor shift +#define LCD_DISPLAYMOVE 0x08 +#define LCD_CURSORMOVE 0x00 +#define LCD_MOVERIGHT 0x04 +#define LCD_MOVELEFT 0x00 + +// flags for function set +#define LCD_8BITMODE 0x10 +#define LCD_4BITMODE 0x00 +#define LCD_2LINE 0x08 +#define LCD_1LINE 0x00 +#define LCD_5x10DOTS 0x04 +#define LCD_5x8DOTS 0x00 + +// flags for backlight control +#define LCD_BACKLIGHT 0x08 +#define LCD_NOBACKLIGHT 0x00 + +#define En 0b00000100 // Enable bit +#define Rw 0b00000010 // Read/Write bit +#define Rs 0b00000001 // Register select bit + +class LiquidCrystal_I2C : public Print { +public: + LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows); + void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS ); + void clear(); + void home(); + void noDisplay(); + void display(); + void noBlink(); + void blink(); + void noCursor(); + void cursor(); + void scrollDisplayLeft(); + void scrollDisplayRight(); + void printLeft(); + void printRight(); + void leftToRight(); + void rightToLeft(); + void shiftIncrement(); + void shiftDecrement(); + void noBacklight(); + void backlight(); + void autoscroll(); + void noAutoscroll(); + void createChar(uint8_t, uint8_t[]); + void createChar(uint8_t location, const char *charmap); + // Example: const char bell[8] PROGMEM = {B00100,B01110,B01110,B01110,B11111,B00000,B00100,B00000}; + + void setCursor(uint8_t, uint8_t); + size_t write(uint8_t); + void command(uint8_t); + void init(); + void oled_init(); + +////compatibility API function aliases +void blink_on(); // alias for blink() +void blink_off(); // alias for noBlink() +void cursor_on(); // alias for cursor() +void cursor_off(); // alias for noCursor() +void setBacklight(uint8_t new_val); // alias for backlight() and nobacklight() +void load_custom_character(uint8_t char_num, uint8_t *rows); // alias for createChar() +void printstr(const char[]); + +////Unsupported API functions (not implemented in this library) +uint8_t status(); +void setContrast(uint8_t new_val); +uint8_t keypad(); +void setDelay(int,int); +void on(); +void off(); +uint8_t init_bargraph(uint8_t graphtype); +void draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end); +void draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end); + + +private: + void init_priv(); + void send(uint8_t, uint8_t); + void write4bits(uint8_t); + void expanderWrite(uint8_t); + void pulseEnable(uint8_t); + uint8_t _Addr; + uint8_t _displayfunction; + uint8_t _displaycontrol; + uint8_t _displaymode; + uint8_t _numlines; + bool _oled; + uint8_t _cols; + uint8_t _rows; + uint8_t _backlightval; +}; + +#endif diff --git a/examples/AllProtocolsOnLCD/PinDefinitionsAndMore.h b/examples/AllProtocolsOnLCD/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/AllProtocolsOnLCD/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/BoseWaveSendDemo/BoseWaveSendDemo.ino b/examples/BoseWaveSendDemo/BoseWaveSendDemo.ino deleted file mode 100644 index fde962aea..000000000 --- a/examples/BoseWaveSendDemo/BoseWaveSendDemo.ino +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Based on IRremote: IRsendDemo by Ken Shirriff - * - * Prompt user for a code to send. Make sure your 940-950nm IR LED is - * connected to the default digital output. Place your Bose Wave Radio - * CD in the line of sight of your LED, and send commands! - */ -#include - -IRsend IrSender; - -// On the Zero and others we switch explicitly to SerialUSB -#if defined(ARDUINO_ARCH_SAMD) -#define Serial SerialUSB -#endif - -bool prompt; -void menu(); - -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - - prompt = true; -} - -void loop() { - if (prompt) { - menu(); - } - prompt = false; - - if (Serial.available()) { - int answer = Serial.read(); - if (answer == -1) { - delay(300); - } else if (answer == 48) { // 0 - IrSender.sendBoseWave(0xFF); // On/Off - prompt = true; - } else if (answer == 49) { // 1 - IrSender.sendBoseWave(0xFD); // Volume Up - prompt = true; - } else if (answer == 50) { // 2 - IrSender.sendBoseWave(0xFC); // Volume Down - prompt = true; - } else if (answer == 51) { // 3 - IrSender.sendBoseWave(0xF4); // Tune Up - prompt = true; - } else if (answer == 52) { // 4 - IrSender.sendBoseWave(0xF3); // Tune Down - prompt = true; - } else if (answer == 53) { // 5 - IrSender.sendBoseWave(0xF7); // AM - prompt = true; - } else if (answer == 54) { // 6 - IrSender.sendBoseWave(0xF9); // FM - prompt = true; - } else if (answer == 55) { // 7 - IrSender.sendBoseWave(0xF2); // Preset 1 - prompt = true; - } else if (answer == 56) { // 8 - IrSender.sendBoseWave(0xF1); // Preset 2 - prompt = true; - } else if (answer == 57) { // 9 - IrSender.sendBoseWave(0xF0); // Preset 3 - prompt = true; - } else if (answer == 97) { // a - IrSender.sendBoseWave(0xEF); // Preset 4 - prompt = true; - } else if (answer == 98) { // b - IrSender.sendBoseWave(0xEE); // Preset 5 - prompt = true; - } else if (answer == 99) { // c - IrSender.sendBoseWave(0xFB); // Preset 6 - prompt = true; - } else if (answer == 100) { // d - IrSender.sendBoseWave(0xFE); // Mute - prompt = true; - } else if (answer == 101) { // e - IrSender.sendBoseWave(0xF6); // Pause - prompt = true; - } else if (answer == 102) { // f - IrSender.sendBoseWave(0xF5); // Stop - prompt = true; - } else if (answer == 103) { // g - IrSender.sendBoseWave(0xF8); // Aux - prompt = true; - } else if (answer == 104) { // h - IrSender.sendBoseWave(0xFA); // Sleep - prompt = true; - } - delay(300); - } -} - -void menu() { - Serial.println("0: On / Off"); - Serial.println("1: Volume Up"); - Serial.println("2: Volume Down"); - Serial.println("3: Tune Up"); - Serial.println("4: Tune Down"); - Serial.println("5: AM"); - Serial.println("6: FM"); - Serial.println("7: Preset 1"); - Serial.println("8: Preset 2"); - Serial.println("9: Preset 3"); - Serial.println("a: Preset 4"); - Serial.println("b: Preset 5"); - Serial.println("c: Preset 6"); - Serial.println("d: Mute"); - Serial.println("e: Play/Pause"); - Serial.println("f: Stop"); - Serial.println("g: Aux"); - Serial.println("h: Sleep"); -} diff --git a/examples/ControlRelay/ControlRelay.ino b/examples/ControlRelay/ControlRelay.ino new file mode 100644 index 000000000..0ea2e73aa --- /dev/null +++ b/examples/ControlRelay/ControlRelay.ino @@ -0,0 +1,108 @@ +/* + * ControlRelay.cpp + * + * Toggles an output pin at each command received + * An IR detector/demodulator must be connected to the input RECV_PIN. + * Initially coded 2009 Ken Shirriff http://www.righto.com + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2009-2021 Ken Shirriff, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. + +#if FLASHEND <= 0x1FFF || (defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF) // For 8k flash or 512 bytes RAM or less, like ATtiny85, ATtiny167 +#define EXCLUDE_UNIVERSAL_PROTOCOLS // Saves up to 1000 bytes program memory. +#define EXCLUDE_EXOTIC_PROTOCOLS +#endif + +#include + +#if defined(APPLICATION_PIN) +#define RELAY_PIN APPLICATION_PIN +#else +#define RELAY_PIN 5 +#endif + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + pinMode(RELAY_PIN, OUTPUT); + + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); +} + +int on = 0; +unsigned long last = millis(); + +void loop() { + if (IrReceiver.decode()) { + // If it's been at least 1/4 second since the last + // IR received, toggle the relay + if (millis() - last > 250) { + on = !on; + Serial.print(F("Switch relay ")); + if (on) { + digitalWrite(RELAY_PIN, HIGH); + Serial.println(F("on")); + } else { + digitalWrite(RELAY_PIN, LOW); + Serial.println(F("off")); + } + +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604 + IrReceiver.printIRResultShort(&Serial); + IrReceiver.printIRSendUsage(&Serial); + Serial.println(); + if (IrReceiver.decodedIRData.protocol == UNKNOWN) { + // We have an unknown protocol, print more info + Serial.println(F("Received noise or an unknown (or not yet enabled) protocol")); + IrReceiver.printIRResultRawFormatted(&Serial, true); + } +#else + // Print a minimal summary of received data + IrReceiver.printIRResultMinimal(&Serial); + Serial.println(); +#endif // FLASHEND + } + last = millis(); + IrReceiver.resume(); // Enable receiving of the next value + } +} diff --git a/examples/ControlRelay/PinDefinitionsAndMore.h b/examples/ControlRelay/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/ControlRelay/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/IR2Keyboard/IR2Keyboard.ino b/examples/IR2Keyboard/IR2Keyboard.ino deleted file mode 100644 index 2e8b78d28..000000000 --- a/examples/IR2Keyboard/IR2Keyboard.ino +++ /dev/null @@ -1,230 +0,0 @@ -/* - this is just an example of creative (maybe useful) use of IR in a PC, as a media center or some other automatizations. - the idea is fordward the IR commands (from any spare remote you may have) to keyboard pulses, combos, shortcuts, etc. - Keyboard funtions only available on "leonardo" and "micro". for more keys read into https://www.arduino.cc/en/Reference/KeyboardModifiers - for IR decoding im using the amazing lib from: http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html - Remote class emulate media keys. need to be added manually to the arduino lib. http://stefanjones.ca/blog/arduino-leonardo-remote-multimedia-keys/ - this is optional, you dont need Remote class to run the IR or Keyboard; its only for media keys - */ - -/* - Remote methods: - http://stefanjones.ca/blog/arduino-leonardo-remote-multimedia-keys/ - Volume - Remote.increase(void); - Remote.decrease(void); - Remote.mute(void); - Playback - Remote.play(void); - Remote.pause(void); - Remote.stop(void); - Track Controls - Remote.next(void); - Remote.previous(void); - Remote.forward(void); - Remote.rewind(void); - Send an empty report to prevent repeated actions - Keyboard.releaseAll(); - */ - -#include -#include -#define T 15 - -bool rotation = false; - -#if defined(ESP32) -int IR_RECEIVE_PIN = 15; -#elif defined(ARDUINO_AVR_PROMICRO) -int IR_RECEIVE_PIN = 10; -#else -int IR_RECEIVE_PIN = 11; -#endif -IRrecv IrReceiver(IR_RECEIVE_PIN); - -// On the Zero and others we switch explicitly to SerialUSB -#if defined(ARDUINO_ARCH_SAMD) -#define Serial SerialUSB -#endif - -void PanasonicRemote(); - -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - - Keyboard.begin(); - // In case the interrupt driver crashes on setup, give a clue - // to the user what's going on. - Serial.println("Enabling IRin"); - IrReceiver.enableIRIn(); // Start the receiver - IrReceiver.blink13(true); // Enable feedback LED - - Serial.print(F("Ready to receive IR signals at pin ")); - Serial.println(IR_RECEIVE_PIN); -} - -void loop() { - if (IrReceiver.decode()) { - //DellRemote(results.value); - PanasonicRemote(); - //mapping(); - delay(300); - IrReceiver.resume(); // Receive the next value - } - delay(100); -} - -void mapping() { - uint32_t tCode = IrReceiver.results.value; - //use this function to map the codes of all your buttons. some buttons send different codes if they are hold down - if ((tCode == 0x801C2B2E) or (tCode == 0xB4AFB411) or (tCode == 0xD17D8037)) { - Serial.println("forward button"); - } else if ((tCode == 0xB7AFB8C8) or (tCode == 0xD07D7EA2) or (tCode == 0x801CAB2F)) { - Serial.println("previous button"); - } else { - Serial.print(IrReceiver.results.value, HEX); - Serial.println("unknown code!"); - } -} - -void PanasonicRemote() { - uint32_t code = IrReceiver.results.value; - - //here i have mapped some buttons of my blue ray remote. - if (code == 0xD00A0AD) {/*forward*/ - Keyboard.releaseAll(); - delay(T); - Keyboard.press(KEY_LEFT_ALT); - Keyboard.press(KEY_RIGHT_ARROW); - delay(T); - Keyboard.releaseAll(); /*Remote.forward(); Keyboard.releaseAll(); */ - } //Ctrl + -> forward 10 seconds VLC - else if (code == 0xD00202D) {/*atras*/ - Keyboard.releaseAll(); - delay(T); - Keyboard.press(KEY_LEFT_ALT); - Keyboard.press(KEY_LEFT_ARROW); - delay(T); - Keyboard.releaseAll(); /*Remote.rewind(); Keyboard.releaseAll(); */ - } //Ctrl + <- rewind 10 seconds VLC - else if (code == 0xD00525F) {/*next*/ - Keyboard.releaseAll(); - delay(T); -// Remote.next(); - Keyboard.releaseAll(); - } else if (code == 0xD00929F) {/*prev*/ - Keyboard.releaseAll(); - delay(T); -// Remote.previous(); - Keyboard.releaseAll(); - } - //else if (code == 0xD00000D){/*stop*/ Keyboard.releaseAll();delay(T); Remote.stop(); Keyboard.releaseAll(); } - else if (code == 0x1000405) {/*vol +*/ - Keyboard.releaseAll(); - delay(T); -// Remote.increase(); - Keyboard.releaseAll(); - } else if (code == 0x1008485) {/*vol -*/ - Keyboard.releaseAll(); - delay(T); -// Remote.decrease(); - Keyboard.releaseAll(); - } else if (code == 0xD00BCB1) {/*POWER*/ - Keyboard.releaseAll(); - delay(T); -// Remote.mute(); - Keyboard.releaseAll(); - } else if ((code == 0xD00606D) or (code == 0xD00505D)) {/*play/pause*/ - Keyboard.releaseAll(); - delay(T); -// Remote.play(); - Keyboard.releaseAll(); - } else if (code == 0xD00818C) {/*return*/ - Keyboard.releaseAll(); - delay(T); - Keyboard.write(KEY_ESC); - } else if (code == 0xD00414C) {/*OK*/ - Keyboard.releaseAll(); - delay(T); - Keyboard.print(" "); /*evitar doble pulsaciondelay(300);*/ - } else if (code == 0x100BCBD) {/*tvPower*/ - Keyboard.releaseAll(); - delay(T); - Keyboard.press(KEY_LEFT_CTRL); - Keyboard.press(KEY_UP_ARROW); - delay(T); - Keyboard.releaseAll(); - } //volumen en VLC - else if (code == 0x100A0A1) {/*input AV*/ - Keyboard.releaseAll(); - delay(T); - Keyboard.press(KEY_LEFT_CTRL); - Keyboard.press(KEY_DOWN_ARROW); - delay(T); - Keyboard.releaseAll(); - } //volumen en VLC - else if (code == 0xD00808D) {/*open/close*/ - Keyboard.releaseAll(); - delay(T); - Keyboard.press(KEY_LEFT_CTRL); - Keyboard.press(KEY_PAGE_UP); - delay(T); - Keyboard.releaseAll(); - } //cambiar canal HEXCHAT - else if (code == 0xD004944) {/*display*/ - Keyboard.releaseAll(); - delay(T); - Keyboard.press(KEY_LEFT_CTRL); - Keyboard.press(KEY_PAGE_DOWN); - delay(T); - Keyboard.releaseAll(); - } //cambiar canal HEXCHAT - else if (code == 0xD803AB7) {/*DLNA*/ - if (rotation) { - Keyboard.releaseAll(); - rotation = false; - delay(T); - } else { - Keyboard.releaseAll(); - delay(T); - Keyboard.press(KEY_LEFT_GUI); - delay(T); - Keyboard.write(KEY_TAB); - rotation = true; - } - } - //rotation mode //iniciar animacion de rotation de ventanas //start window rotation with compiz - else if (code == 0xD00A1AC) {/*arrow up*/ - Keyboard.write(KEY_UP_ARROW); - } else if (code == 0xD00616C) {/*arrow down*/ - Keyboard.releaseAll(); - delay(T); - Keyboard.write(KEY_DOWN_ARROW); - } else if (code == 0xD00E1EC) {/*arrow right*/ - Keyboard.write(KEY_LEFT_ARROW); - } else if (code == 0xD00111C) {/*arrow left*/ - Keyboard.write(KEY_RIGHT_ARROW); - } else if (code == 0xD00D9D4) {/*TOP MENU*/ - Keyboard.print("f"); - } //f pone a pantalla completa en VLC fullscreen - else if (code == 0xD00010C) {/*S sub menu*/ - Keyboard.print("v"); - } //v cambia subs en VLC change subs - else if (code == 0xD00CCC1) {/*AUDIO*/ - Keyboard.print("b"); - } //b cambia audio en VLC change audio track - else if (code == 0xD002825) {/*#5*/ - Keyboard.write(KEY_RETURN); - } //useful to press enter on the file you want to play. - else { - Serial.print(IrReceiver.results.value, HEX); - Serial.println(" Unknown!"); - } -} diff --git a/examples/IRDispatcherDemo/DemoIRCommandMapping.h b/examples/IRDispatcherDemo/DemoIRCommandMapping.h new file mode 100644 index 000000000..26110da23 --- /dev/null +++ b/examples/IRDispatcherDemo/DemoIRCommandMapping.h @@ -0,0 +1,193 @@ +/* + * DemoIRCommandMapping.h + * + * IR remote button codes, strings, and functions to call + * + * Copyright (C) 2019-2022 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + */ + +#ifndef _IR_COMMAND_MAPPING_H +#define _IR_COMMAND_MAPPING_H + +#include +//#include "Commands.h" // includes all the commands used in the mapping arrays below + +/* + * !!! Choose your remote !!! + */ +//#define USE_KEYES_REMOTE_CLONE With number pad and direction control switched, will be taken as default +//#define USE_KEYES_REMOTE +#if !defined(USE_KEYES_REMOTE) && !defined(USE_KEYES_REMOTE_CLONE) +#define USE_KEYES_REMOTE_CLONE // the one you can buy at aliexpress +#endif + +#if (defined(USE_KEYES_REMOTE) && defined(USE_KEYES_REMOTE_CLONE)) +#error "Please choose only one remote for compile" +#endif + +#if defined(USE_KEYES_REMOTE_CLONE) +#define IR_REMOTE_NAME "KEYES_CLONE" +// Codes for the KEYES CLONE remote control with 17 keys with number pad above direction control +#if defined(USE_IRMP_LIBRARY) +#define IR_ADDRESS 0xFF00 // IRMP interprets NEC addresses always as 16 bit +#else +#define IR_ADDRESS 0x00 +#endif + +#define IR_UP 0x18 +#define IR_DOWN 0x52 +#define IR_RIGHT 0x5A +#define IR_LEFT 0x08 +#define IR_OK 0x1C + +#define IR_1 0x45 +#define IR_2 0x46 +#define IR_3 0x47 +#define IR_4 0x44 +#define IR_5 0x40 +#define IR_6 0x43 +#define IR_7 0x07 +#define IR_8 0x15 +#define IR_9 0x09 +#define IR_0 0x19 + +#define IR_STAR 0x16 +#define IR_HASH 0x0D +/* + * SECOND: + * IR button to command mapping for better reading. IR buttons should only referenced here. + */ +#define COMMAND_ON IR_UP +#define COMMAND_OFF IR_DOWN +#define COMMAND_INCREASE_BLINK IR_RIGHT +#define COMMAND_DECREASE_BLINK IR_LEFT + +#define COMMAND_START IR_OK +#define COMMAND_STOP IR_HASH +#define COMMAND_RESET IR_STAR +#define COMMAND_BLINK IR_0 +#define COMMAND_TONE1 IR_1 + +#define COMMAND_TONE2 IR_2 +#define COMMAND_TONE3 IR_3 +//#define IR_4 +//#define IR_5 +//#define IR_6 +//#define IR_7 +//#define IR_8 +//#define IR_9 + +#endif + +#if defined(USE_KEYES_REMOTE) +#define IR_REMOTE_NAME "KEYES" +/* + * FIRST: + * IR code to button mapping for better reading. IR codes should only referenced here. + */ +// Codes for the KEYES remote control with 17 keys and direction control above number pad +#if defined(USE_IRMP_LIBRARY) +#define IR_ADDRESS 0xFF00 // IRMP interprets NEC addresses always as 16 bit +#else +#define IR_ADDRESS 0x00 +#endif + +#define IR_UP 0x46 +#define IR_DOWN 0x15 +#define IR_RIGHT 0x43 +#define IR_LEFT 0x44 +#define IR_OK 0x40 + +#define IR_1 0x16 +#define IR_2 0x19 +#define IR_3 0x0D +#define IR_4 0x0C +#define IR_5 0x18 +#define IR_6 0x5E +#define IR_7 0x08 +#define IR_8 0x1C +#define IR_9 0x5A +#define IR_0 0x52 + +#define IR_STAR 0x42 +#define IR_HASH 0x4A + +/* + * SECOND: + * IR button to command mapping for better reading. IR buttons should only referenced here. + */ +#define COMMAND_ON IR_UP +#define COMMAND_OFF IR_DOWN +#define COMMAND_INCREASE_BLINK IR_RIGHT +#define COMMAND_DECREASE_BLINK IR_LEFT + +#define COMMAND_RESET IR_OK +#define COMMAND_STOP IR_HASH +#define COMMAND_STOP IR_STAR +#define COMMAND_BLINK IR_0 +#define COMMAND_TONE2 IR_1 + +#define COMMAND_TONE1 IR_2 +#define COMMAND_TONE2 IR_3 +#define COMMAND_TONE2 IR_4 +#define COMMAND_TONE2 IR_5 +#define COMMAND_TONE2 IR_6 +#define COMMAND_TONE2 IR_7 +#define COMMAND_TONE2 IR_8 +#define COMMAND_TONE2 IR_9 +#endif + +/* + * THIRD: + * Main mapping of commands to C functions + */ + +// IR strings of functions for output +static const char LEDon[] PROGMEM ="LED on"; +static const char LEDoff[] PROGMEM ="LED off"; + +static const char blink20times[] PROGMEM ="blink 20 times"; +static const char blinkStart[] PROGMEM ="blink start"; + +static const char increaseBlink[] PROGMEM ="increase blink frequency"; +static const char decreaseBlink[] PROGMEM ="decrease blink frequency"; + +static const char tone2200[] PROGMEM ="tone 2200"; +static const char tone1800[] PROGMEM ="tone 1800"; +static const char printMenu[] PROGMEM ="printMenu"; + +static const char reset[] PROGMEM ="reset"; +static const char stop[] PROGMEM ="stop"; + +// not used yet +static const char test[] PROGMEM ="test"; +static const char pattern[] PROGMEM ="pattern"; +static const char unknown[] PROGMEM ="unknown"; + +/* + * Main mapping array of commands to C functions and command strings + */ +const struct IRToCommandMappingStruct IRMapping[] = { /**/ +{ COMMAND_BLINK, IR_COMMAND_FLAG_BLOCKING, &doLedBlink20times, blink20times }, /**/ +{ COMMAND_STOP, IR_COMMAND_FLAG_BLOCKING, &doStop, stop }, + +/* + * Short commands, which can be executed always + */ +{ COMMAND_TONE1, IR_COMMAND_FLAG_BLOCKING, &doTone1800, tone1800 }, /**/ +{ COMMAND_TONE3, IR_COMMAND_FLAG_BLOCKING, &doPrintMenu, printMenu }, /**/ +{ COMMAND_ON, IR_COMMAND_FLAG_NON_BLOCKING, &doLedOn, LEDon }, /**/ +{ COMMAND_OFF, IR_COMMAND_FLAG_NON_BLOCKING, &doLedOff, LEDoff }, /**/ +{ COMMAND_START, IR_COMMAND_FLAG_NON_BLOCKING, &doLedBlinkStart, blinkStart }, /**/ +{ COMMAND_RESET, IR_COMMAND_FLAG_NON_BLOCKING, &doResetBlinkFrequency, reset }, + +/* + * Repeatable short commands + */ +{ COMMAND_TONE2, IR_COMMAND_FLAG_REPEATABLE_NON_BLOCKING, &doTone2200, tone2200 }, /**/ +{ COMMAND_INCREASE_BLINK, IR_COMMAND_FLAG_REPEATABLE_NON_BLOCKING, &doIncreaseBlinkFrequency, increaseBlink }, /**/ +{ COMMAND_DECREASE_BLINK, IR_COMMAND_FLAG_REPEATABLE_NON_BLOCKING, &doDecreaseBlinkFrequency, decreaseBlink } }; + +#endif // _IR_COMMAND_MAPPING_H diff --git a/examples/IRDispatcherDemo/IRCommandDispatcher.h b/examples/IRDispatcherDemo/IRCommandDispatcher.h new file mode 100644 index 000000000..83a98b27c --- /dev/null +++ b/examples/IRDispatcherDemo/IRCommandDispatcher.h @@ -0,0 +1,138 @@ +/* + * IRCommandDispatcher.h + * + * Library to process IR commands by calling functions specified in a mapping array. + * + * To run this example you need to install the "IRremote" or "IRMP" library under "Tools -> Manage Libraries..." or "Ctrl+Shift+I" + * + * Copyright (C) 2019-2024 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of ServoEasing https://github.com/ArminJo/ServoEasing. + * This file is part of IRMP https://github.com/IRMP-org/IRMP. + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * IRCommandDispatcher is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _IR_COMMAND_DISPATCHER_H +#define _IR_COMMAND_DISPATCHER_H + +#include + +/* + * For command mapping file + */ +#define IR_COMMAND_FLAG_BLOCKING 0x00 // default - blocking command, repeat not accepted, only one command at a time. Stops an already running command. +#define IR_COMMAND_FLAG_REPEATABLE 0x01 // repeat accepted +#define IR_COMMAND_FLAG_NON_BLOCKING 0x02 // Non blocking (short) command that can be processed any time and may interrupt other IR commands - used for stop, set direction etc. +#define IR_COMMAND_FLAG_REPEATABLE_NON_BLOCKING (IR_COMMAND_FLAG_REPEATABLE | IR_COMMAND_FLAG_NON_BLOCKING) +#define IR_COMMAND_FLAG_BEEP 0x04 // Do a single short beep before executing command. May not be useful for short or repeating commands. +#define IR_COMMAND_FLAG_BLOCKING_BEEP (IR_COMMAND_FLAG_BLOCKING | IR_COMMAND_FLAG_BEEP) + + +#if !defined(IS_STOP_REQUESTED) +#define IS_STOP_REQUESTED IRDispatcher.requestToStopReceived +#endif +#if !defined(RETURN_IF_STOP) +#define RETURN_IF_STOP if (IRDispatcher.requestToStopReceived) return +#endif +#if !defined(BREAK_IF_STOP) +#define BREAK_IF_STOP if (IRDispatcher.requestToStopReceived) break +#endif +#if !defined(DELAY_AND_RETURN_IF_STOP) +#define DELAY_AND_RETURN_IF_STOP(aDurationMillis) if (IRDispatcher.delayAndCheckForStop(aDurationMillis)) return +#endif + +// Basic mapping structure +struct IRToCommandMappingStruct { +#if defined(IR_COMMAND_HAS_MORE_THAN_8_BIT) + uint16_t IRCode; +#else + uint8_t IRCode; +#endif + uint8_t Flags; + void (*CommandToCall)(); + const char *CommandString; +}; + +struct IRDataForCommandDispatcherStruct { + uint16_t address; // to distinguish between multiple senders +#if defined(IR_COMMAND_HAS_MORE_THAN_8_BIT) + uint16_t command; +#else + uint8_t command; +#endif + bool isRepeat; + uint32_t MillisOfLastCode; // millis() of last IR command -including repeats!- received - for timeouts etc. + volatile bool isAvailable; // flag for a polling interpreting function, that a new command has arrived. Is set true by library and set false by main loop. +}; + +/* + * Special codes (hopefully) not sent by the remote - otherwise please redefine it here + */ +#if defined(IR_COMMAND_HAS_MORE_THAN_8_BIT) +#define COMMAND_EMPTY 0xFFFF // code no command +#else +#define COMMAND_EMPTY 0xFF // code no command +#endif + +class IRCommandDispatcher { +public: + void init(); + void printIRInfo(Print *aSerial); + + bool checkAndRunNonBlockingCommands(); + bool checkAndRunSuspendedBlockingCommands(); +#if defined(IR_COMMAND_HAS_MORE_THAN_8_BIT) + void setNextBlockingCommand(uint16_t aBlockingCommandToRunNext); +#else + void setNextBlockingCommand(uint8_t aBlockingCommandToRunNext); +#endif + bool delayAndCheckForStop(uint16_t aDelayMillis); + + // The main dispatcher function + void checkAndCallCommand(bool aCallBlockingCommandImmediately); + + void printIRCommandString(Print *aSerial); + void setRequestToStopReceived(bool aRequestToStopReceived = true); + +#if defined(IR_COMMAND_HAS_MORE_THAN_8_BIT) + uint16_t currentBlockingCommandCalled = COMMAND_EMPTY; // The code for the current called command + uint16_t lastBlockingCommandCalled = COMMAND_EMPTY; // The code for the last called command. Can be evaluated by main loop + uint16_t BlockingCommandToRunNext = COMMAND_EMPTY; // Storage for command currently suspended to allow the current command to end, before it is called by main loop +#else + uint8_t currentBlockingCommandCalled = COMMAND_EMPTY; // The code for the current called command + uint8_t lastBlockingCommandCalled = COMMAND_EMPTY; // The code for the last called command. Can be evaluated by main loop + uint8_t BlockingCommandToRunNext = COMMAND_EMPTY; // Storage for command currently suspended to allow the current command to end, before it is called by main loop +#endif + bool justCalledBlockingCommand = false; // Flag that a blocking command was received and called - is set before call of command + /* + * Flag for running blocking commands to terminate. To check, you can use "if (IRDispatcher.requestToStopReceived) return;" (available as macro RETURN_IF_STOP). + * It is set if a blocking IR command received, which cannot be executed directly. Can be reset by main loop, if command has stopped. + * It is reset before executing a blocking command. + */ + volatile bool requestToStopReceived; + /* + * This flag must be true, if we have a function, which want to interpret the IR codes by itself e.g. the calibrate function of QuadrupedControl + */ + bool doNotUseDispatcher = false; + + struct IRDataForCommandDispatcherStruct IRReceivedData; + +}; + +extern IRCommandDispatcher IRDispatcher; + +#endif // _IR_COMMAND_DISPATCHER_H diff --git a/examples/IRDispatcherDemo/IRCommandDispatcher.hpp b/examples/IRDispatcherDemo/IRCommandDispatcher.hpp new file mode 100644 index 000000000..a8006400e --- /dev/null +++ b/examples/IRDispatcherDemo/IRCommandDispatcher.hpp @@ -0,0 +1,366 @@ +/* + * IRCommandDispatcher.hpp + * + * Library to process IR commands by calling functions specified in a mapping array. + * Commands can be tagged as blocking or non blocking. + * + * To run this example you need to install the "IRremote" or "IRMP" library. + * Install it under "Tools -> Manage Libraries..." or "Ctrl+Shift+I" + * + * The IR library calls a callback function, which executes a non blocking command directly in ISR (Interrupt Service Routine) context! + * A blocking command is stored and sets a stop flag for an already running blocking function to terminate. + * The blocking command can in turn be executed by main loop by calling IRDispatcher.checkAndRunSuspendedBlockingCommands(). + * + * Copyright (C) 2019-2024 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of ServoEasing https://github.com/ArminJo/ServoEasing. + * This file is part of IRMP https://github.com/IRMP-org/IRMP. + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * IRCommandDispatcher is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * Program behavior is modified by the following macros + * USE_TINY_IR_RECEIVER + * USE_IRMP_LIBRARY + * IR_COMMAND_HAS_MORE_THAN_8_BIT + */ + +#ifndef _IR_COMMAND_DISPATCHER_HPP +#define _IR_COMMAND_DISPATCHER_HPP + +#include + +#include "IRCommandDispatcher.h" + +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif +/* + * Enable this to see information on each call. + * Since there should be no library which uses Serial, it should only be enabled for development purposes. + */ +#if defined(INFO) && !defined(LOCAL_INFO) +#define LOCAL_INFO +#else +//#define LOCAL_INFO // This enables info output only for this file +#endif +#if defined(DEBUG) +#define LOCAL_DEBUG +// Propagate debug level +#define LOCAL_INFO +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +IRCommandDispatcher IRDispatcher; + +#if defined(LOCAL_INFO) +#define CD_INFO_PRINT(...) Serial.print(__VA_ARGS__); +#define CD_INFO_PRINTLN(...) Serial.println(__VA_ARGS__); +#else +#define CD_INFO_PRINT(...) void(); +#define CD_INFO_PRINTLN(...) void(); +#endif + +#if defined(USE_TINY_IR_RECEIVER) +#define USE_CALLBACK_FOR_TINY_RECEIVER // Call the user provided function "void handleReceivedTinyIRData()" each time a frame or repeat is received. +#include "TinyIRReceiver.hpp" // included in "IRremote" library + +void IRCommandDispatcher::init() { + initPCIInterruptForTinyReceiver(); +} + +/* + * @return true, if IR Receiver is attached + */ +void IRCommandDispatcher::printIRInfo(Print *aSerial) { + aSerial->println(); + // For available IR commands see IRCommandMapping.h https://github.com/ArminJo/PWMMotorControl/blob/master/examples/SmartCarFollower/IRCommandMapping.h + aSerial->print(F("Listening to IR remote of type ")); + aSerial->print(IR_REMOTE_NAME); + aSerial->println(F(" at pin " STR(IR_RECEIVE_PIN))); +} + +/* + * This is the TinyReceiver callback function, which is called if a complete command was received + * It checks for right address and then call the dispatcher + */ +# if defined(ESP8266) || defined(ESP32) +IRAM_ATTR +# endif +void handleReceivedTinyIRData() { + IRDispatcher.IRReceivedData.address = TinyIRReceiverData.Address; + IRDispatcher.IRReceivedData.command = TinyIRReceiverData.Command; + IRDispatcher.IRReceivedData.isRepeat = TinyIRReceiverData.Flags & IRDATA_FLAGS_IS_REPEAT; + IRDispatcher.IRReceivedData.MillisOfLastCode = millis(); + +# if defined(LOCAL_INFO) + printTinyReceiverResultMinimal(&Serial); +# endif + + if (TinyIRReceiverData.Address == IR_ADDRESS) { // IR_ADDRESS is defined in *IRCommandMapping.h + IRDispatcher.IRReceivedData.isAvailable = true; + if(!IRDispatcher.doNotUseDispatcher) { + /* + * Only short (non blocking) commands are executed directly in ISR (Interrupt Service Routine) context, + * others are stored for main loop which calls checkAndRunSuspendedBlockingCommands() + */ + IRDispatcher.checkAndCallCommand(false); + } + + } else { + CD_INFO_PRINT(F("Wrong address. Expected 0x")); + CD_INFO_PRINTLN(IR_ADDRESS, HEX); + } +} + +#elif defined(USE_IRMP_LIBRARY) +# if !defined(IRMP_USE_COMPLETE_CALLBACK) +# error IRMP_USE_COMPLETE_CALLBACK must be activated for IRMP library +# endif + +void IRCommandDispatcher::init() { + irmp_init(); +} + +/* + * This is the callback function, which is called if a complete command was received + */ +#if defined(ESP8266) || defined(ESP32) +IRAM_ATTR +#endif +void handleReceivedIRData() { + IRMP_DATA tTeporaryData; + irmp_get_data(&tTeporaryData); + IRDispatcher.IRReceivedData.address = tTeporaryData.address; + IRDispatcher.IRReceivedData.command = tTeporaryData.command; + IRDispatcher.IRReceivedData.isRepeat = tTeporaryData.flags & IRMP_FLAG_REPETITION; + IRDispatcher.IRReceivedData.MillisOfLastCode = millis(); + + CD_INFO_PRINT(F("A=0x")); + CD_INFO_PRINT(IRDispatcher.IRReceivedData.address, HEX); + CD_INFO_PRINT(F(" C=0x")); + CD_INFO_PRINT(IRDispatcher.IRReceivedData.command, HEX); + if (IRDispatcher.IRReceivedData.isRepeat) { + CD_INFO_PRINT(F("R")); + } + CD_INFO_PRINTLN(); + + // To enable delay() for commands +# if !defined(ARDUINO_ARCH_MBED) + interrupts(); // be careful with always executable commands which lasts longer than the IR repeat duration. +# endif + + if (IRDispatcher.IRReceivedData.address == IR_ADDRESS) { + IRDispatcher.checkAndCallCommand(true); + } else { + CD_INFO_PRINT(F("Wrong address. Expected 0x")); + CD_INFO_PRINTLN(IR_ADDRESS, HEX); + } +} +#endif // elif defined(USE_IRMP_LIBRARY) + +/* + * The main dispatcher function called by IR-ISR, main loop and checkAndRunSuspendedBlockingCommands() + * Non blocking commands are executed directly, blocking commands are executed if enabled by parameter and no other command is just running. + * Otherwise request to stop (requestToStopReceived) is set and command is stored for main loop to be later execute by checkAndRunSuspendedBlockingCommands(). + * Sets flags justCalledRegularIRCommand, executingBlockingCommand, requestToStopReceived + * @param aCallBlockingCommandImmediately Run blocking command directly, if no other command is just running. Should be false if called by ISR in order not to block ISR. + */ +void IRCommandDispatcher::checkAndCallCommand(bool aCallBlockingCommandImmediately) { + if (IRReceivedData.command == COMMAND_EMPTY) { + return; + } + + /* + * Search for command in Array of IRToCommandMappingStruct + */ + for (uint_fast8_t i = 0; i < sizeof(IRMapping) / sizeof(struct IRToCommandMappingStruct); ++i) { + if (IRReceivedData.command == IRMapping[i].IRCode) { + /* + * Command found + */ +#if defined(LOCAL_INFO) + const __FlashStringHelper *tCommandName = reinterpret_cast(IRMapping[i].CommandString); +#endif + /* + * Check for repeat and if repeat is allowed for the current command + */ + if (IRReceivedData.isRepeat && !(IRMapping[i].Flags & IR_COMMAND_FLAG_REPEATABLE)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Repeats of command \"")); + Serial.print(tCommandName); + Serial.println("\" not accepted"); +#endif + return; + } + + /* + * Do not accept recursive call of the same command + */ + if (currentBlockingCommandCalled == IRReceivedData.command) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Recursive command \"")); + Serial.print(tCommandName); + Serial.println("\" not accepted"); +#endif + return; + } + + /* + * Execute commands + */ + bool tIsNonBlockingCommand = (IRMapping[i].Flags & IR_COMMAND_FLAG_NON_BLOCKING); + if (tIsNonBlockingCommand) { + // short command here, just call + CD_INFO_PRINT(F("Run non blocking command: ")); + CD_INFO_PRINTLN(tCommandName); +#if defined(BUZZER_PIN) && defined(USE_TINY_IR_RECEIVER) + /* + * Do (non blocking) buzzer feedback before command is executed + */ + if(IRMapping[i].Flags & IR_COMMAND_FLAG_BEEP) { + tone(BUZZER_PIN, 2200, 50); + } +#endif + IRMapping[i].CommandToCall(); + } else { + /* + * Blocking command here + */ + if (aCallBlockingCommandImmediately && currentBlockingCommandCalled == COMMAND_EMPTY) { + /* + * Here no blocking command was running and we are called from main loop + */ + requestToStopReceived = false; // Do not stop the command executed now + justCalledBlockingCommand = true; + currentBlockingCommandCalled = IRReceivedData.command; // set lock for recursive calls + lastBlockingCommandCalled = IRReceivedData.command; // set history, can be evaluated by main loop + /* + * This call is blocking!!! + */ + CD_INFO_PRINT(F("Run blocking command: ")); + CD_INFO_PRINTLN(tCommandName); + +#if defined(BUZZER_PIN) && defined(USE_TINY_IR_RECEIVER) + /* + * Do (non blocking) buzzer feedback before command is executed + */ + if(IRMapping[i].Flags & IR_COMMAND_FLAG_BEEP) { + tone(BUZZER_PIN, 2200, 50); + } +#endif + + IRMapping[i].CommandToCall(); +#if defined(TRACE) + Serial.println(F("End of blocking command")); +#endif + currentBlockingCommandCalled = COMMAND_EMPTY; + } else { + /* + * Called by ISR or another command still running. + * Do not run command directly, but set request to stop to true and store command + * for main loop to execute by checkAndRunSuspendedBlockingCommands() + */ + BlockingCommandToRunNext = IRReceivedData.command; + requestToStopReceived = true; // to stop running command + CD_INFO_PRINT(F("Requested stop and stored blocking command ")); + CD_INFO_PRINT(tCommandName); + CD_INFO_PRINTLN(F(" as next command to run.")); + } + } + break; // command found + } // if (IRReceivedData.command == IRMapping[i].IRCode) + } // for loop + return; +} + +/* + * Intended to be called from main loop + * @return true, if command was called + */ +bool IRCommandDispatcher::checkAndRunSuspendedBlockingCommands() { + /* + * Take last rejected command and call associated function + */ + if (BlockingCommandToRunNext != COMMAND_EMPTY) { + + CD_INFO_PRINT(F("Run stored command=0x")); + CD_INFO_PRINTLN(BlockingCommandToRunNext, HEX); + + IRReceivedData.command = BlockingCommandToRunNext; + BlockingCommandToRunNext = COMMAND_EMPTY; + IRReceivedData.isRepeat = false; + requestToStopReceived = false; // Do not stop the command executed now + checkAndCallCommand(true); + return true; + } + return false; +} + +/* + * Not used internally + */ +#if defined(IR_COMMAND_HAS_MORE_THAN_8_BIT) +void IRCommandDispatcher::setNextBlockingCommand(uint16_t aBlockingCommandToRunNext) +#else +void IRCommandDispatcher::setNextBlockingCommand(uint8_t aBlockingCommandToRunNext) +#endif + { + CD_INFO_PRINT(F("Set next command to run to 0x")); + CD_INFO_PRINTLN(aBlockingCommandToRunNext, HEX); + BlockingCommandToRunNext = aBlockingCommandToRunNext; + requestToStopReceived = true; +} + +/* + * Special delay function for the IRCommandDispatcher. Returns prematurely if requestToStopReceived is set. + * To be used in blocking functions as delay + * @return true - as soon as stop received + */ +bool IRCommandDispatcher::delayAndCheckForStop(uint16_t aDelayMillis) { + uint32_t tStartMillis = millis(); + do { + if (requestToStopReceived) { + return true; + } + } while (millis() - tStartMillis < aDelayMillis); + return false; +} + +void IRCommandDispatcher::printIRCommandString(Print *aSerial) { + for (uint_fast8_t i = 0; i < sizeof(IRMapping) / sizeof(struct IRToCommandMappingStruct); ++i) { + if (IRReceivedData.command == IRMapping[i].IRCode) { + aSerial->println(reinterpret_cast(IRMapping[i].CommandString)); + return; + } + } + aSerial->println(reinterpret_cast(unknown)); +} + +void IRCommandDispatcher::setRequestToStopReceived(bool aRequestToStopReceived) { + requestToStopReceived = aRequestToStopReceived; +} + +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#if defined(LOCAL_INFO) +#undef LOCAL_INFO +#endif +#endif // _IR_COMMAND_DISPATCHER_HPP diff --git a/examples/IRDispatcherDemo/IRDispatcherDemo.ino b/examples/IRDispatcherDemo/IRDispatcherDemo.ino new file mode 100644 index 000000000..6df3321ab --- /dev/null +++ b/examples/IRDispatcherDemo/IRDispatcherDemo.ino @@ -0,0 +1,280 @@ +/* + * IRDispatcherDemo.cpp + * + * Receives NEC IR commands and maps them to different actions by means of a mapping array. + * + * Copyright (C) 2020-2021 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRMP https://github.com/IRMP-org/IRMP. + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * IRMP is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include + +/* + * Choose the library to be used for IR receiving + */ +#define USE_TINY_IR_RECEIVER // Recommended, but only for NEC protocol!!! If disabled and IRMP_INPUT_PIN is defined, the IRMP library is used for decoding +//#define TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT // Requires additional 112 bytes program memory + 4 bytes RAM + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. +// Some kind of auto detect library if USE_TINY_IR_RECEIVER is deactivated +#if !defined(USE_TINY_IR_RECEIVER) +# if defined(IR_RECEIVE_PIN) +#define USE_TINY_IR_RECEIVER +# elif !defined(USE_IRMP_LIBRARY) && defined(IRMP_INPUT_PIN) +#define USE_IRMP_LIBRARY +# else +#error No IR library selected +# endif +#endif + +//#define NO_LED_FEEDBACK_CODE // You can set it here, before the include of IRCommandDispatcher below + +#if defined(USE_TINY_IR_RECEIVER) +//#define NO_LED_FEEDBACK_CODE // Activate this if you want to suppress LED feedback or if you do not have a LED. This saves 14 bytes code and 2 clock cycles per interrupt. + +#elif defined(USE_IRMP_LIBRARY) +/* + * IRMP version + */ +#define IR_RECEIVE_PIN 2 +#define IRMP_USE_COMPLETE_CALLBACK 1 // Enable callback functionality. It is required if IRMP library is used. +#if defined(ALTERNATIVE_IR_FEEDBACK_LED_PIN) +#define FEEDBACK_LED_PIN ALTERNATIVE_IR_FEEDBACK_LED_PIN +#endif + +//#define IRMP_ENABLE_PIN_CHANGE_INTERRUPT // Enable interrupt functionality (not for all protocols) - requires around 376 additional bytes of program memory + +#define IRMP_PROTOCOL_NAMES 1 // Enable protocol number mapping to protocol strings - requires some program memory. Must before #include + +#define IRMP_SUPPORT_NEC_PROTOCOL 1 // this enables only one protocol +//#define IRMP_SUPPORT_KASEIKYO_PROTOCOL 1 + +# if defined(ALTERNATIVE_IR_FEEDBACK_LED_PIN) +#define IRMP_FEEDBACK_LED_PIN ALTERNATIVE_IR_FEEDBACK_LED_PIN +# endif +/* + * After setting the definitions we can include the code and compile it. + */ +#include +void handleReceivedIRData(); +void irmp_tone(uint8_t _pin, unsigned int frequency, unsigned long duration); +#endif // #if defined(USE_IRMP_LIBRARY) + +bool doBlink = false; +uint16_t sBlinkDelay = 200; + +void doPrintMenu(); +void doLedOn(); +void doLedOff(); +void doIncreaseBlinkFrequency(); +void doDecreaseBlinkFrequency(); +void doStop(); +void doResetBlinkFrequency(); +void doLedBlinkStart(); +void doLedBlink20times(); +void doTone1800(); +void doTone2200(); + +/* + * Set definitions and include IRCommandDispatcher library after the declaration of all commands to map + */ +#define INFO // to see some informative output +#include "IRCommandDispatcher.h" // Only for required declarations, the library itself is included below after the definitions of the commands +#include "DemoIRCommandMapping.h" // must be included before IRCommandDispatcher.hpp to define IR_ADDRESS and IRMapping and string "unknown". +#include "IRCommandDispatcher.hpp" + +/* + * Helper macro for getting a macro definition as string + */ +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif +#if defined(ESP8266) + Serial.println(); // to separate it from the internal boot output +#endif + + // Just to know which program is running on my Arduino +#if defined(USE_TINY_IR_RECEIVER) + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing TinyIRReceiver")); +#elif defined(USE_IRREMOTE_LIBRARY) + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing IRremote library version " VERSION_IRREMOTE)); +#elif defined(USE_IRMP_LIBRARY) + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing IRMP library version " VERSION_IRMP)); +#endif + +#if !defined(ESP8266) && !defined(NRF5) + // play feedback tone before setup, since it kills the IR timer settings + tone(TONE_PIN, 1000, 50); + delay(50); +#endif + + IRDispatcher.init(); // This just calls irmp_init() +#if defined(USE_TINY_IR_RECEIVER) + Serial.println(F("Ready to receive NEC IR signals at pin " STR(IR_RECEIVE_PIN))); +#else + irmp_register_complete_callback_function(&handleReceivedIRData); // fixed function in IRCommandDispatcher.hpp + + Serial.print(F("Ready to receive IR signals of protocols: ")); + irmp_print_active_protocols(&Serial); + Serial.println(F("at pin " STR(IRMP_INPUT_PIN))); + +# if defined(ALTERNATIVE_IR_FEEDBACK_LED_PIN) + irmp_irsnd_LEDFeedback(true); // Enable receive signal feedback at ALTERNATIVE_IR_FEEDBACK_LED_PIN + Serial.println(F("IR feedback pin is " STR(ALTERNATIVE_IR_FEEDBACK_LED_PIN))); +# endif +#endif + + Serial.print(F("Listening to commands of IR remote of type ")); + Serial.println(IR_REMOTE_NAME); + doPrintMenu(); +} + +void loop() { + + IRDispatcher.checkAndRunSuspendedBlockingCommands(); + + if (doBlink) { + digitalWrite(LED_BUILTIN, HIGH); + DELAY_AND_RETURN_IF_STOP(sBlinkDelay); + digitalWrite(LED_BUILTIN, LOW); + DELAY_AND_RETURN_IF_STOP(sBlinkDelay); + } + + if (millis() - IRDispatcher.IRReceivedData.MillisOfLastCode > 120000) { + //Short beep as remainder, if we did not receive any command in the last 2 minutes + IRDispatcher.IRReceivedData.MillisOfLastCode += 120000; + doTone1800(); + } + +// delay(10); +} + +void doPrintMenu() { + Serial.println(); + Serial.println(F("Press 1 for tone 1800 Hz")); + Serial.println(F("Press 2 for tone 2200 Hz")); + Serial.println(F("Press 3 for this Menu")); + Serial.println(F("Press 0 for LED blink 20 times")); + Serial.println(F("Press UP for LED on")); + Serial.println(F("Press DOWN for LED off")); + Serial.println(F("Press OK for LED blink start")); + Serial.println(F("Press RIGHT for LED increase blink frequency")); + Serial.println(F("Press LEFT for LED decrease blink frequency")); + Serial.println(F("Press STAR for reset blink frequency")); + Serial.println(F("Press HASH for stop")); + Serial.println(); +} +/* + * Here the actions that are matched to IR keys + */ +void doLedOn() { + digitalWrite(LED_BUILTIN, HIGH); + doBlink = false; +} +void doLedOff() { + digitalWrite(LED_BUILTIN, LOW); + doBlink = false; +} +void doIncreaseBlinkFrequency() { + doBlink = true; + if (sBlinkDelay > 5) { + sBlinkDelay -= sBlinkDelay / 4; + } +} +void doDecreaseBlinkFrequency() { + doBlink = true; + sBlinkDelay += sBlinkDelay / 4; +} +void doStop() { + doBlink = false; +} +void doResetBlinkFrequency() { + sBlinkDelay = 200; + digitalWrite(LED_BUILTIN, LOW); +} +void doLedBlinkStart() { + doBlink = true; +} +/* + * This is a blocking function and checks periodically for stop + */ +void doLedBlink20times() { + for (int i = 0; i < 20; ++i) { + digitalWrite(LED_BUILTIN, HIGH); + DELAY_AND_RETURN_IF_STOP(200); + digitalWrite(LED_BUILTIN, LOW); + DELAY_AND_RETURN_IF_STOP(200); + } +} + +void doTone1800() { +#if defined(USE_IRMP_LIBRARY) && !defined(IRMP_ENABLE_PIN_CHANGE_INTERRUPT) + irmp_tone(TONE_PIN, 1800, 200); +#else +# if !defined(ESP8266) && !defined(NRF5) // tone() stops timer 1 for ESP8266 + tone(TONE_PIN, 1800, 200); +# endif +#endif +} + +void doTone2200() { +#if defined(USE_IRMP_LIBRARY) && !defined(IRMP_ENABLE_PIN_CHANGE_INTERRUPT) + // use IRMP compatible function for tone() + irmp_tone(TONE_PIN, 2200, 50); +#else +# if !defined(ESP8266) && !defined(NRF5) // tone() stops timer 1 for ESP8266 + tone(TONE_PIN, 2200, 50); +# endif +#endif +} + +#if defined(USE_IRMP_LIBRARY) +/* + * Convenience IRMP compatible wrapper function for Arduino tone() if IRMP_ENABLE_PIN_CHANGE_INTERRUPT is NOT activated + * It currently disables the receiving of repeats + */ +void irmp_tone(uint8_t _pin, unsigned int frequency, unsigned long duration) { +# if defined(__AVR__) && !defined(IRMP_ENABLE_PIN_CHANGE_INTERRUPT) + storeIRTimer(); + tone(_pin, frequency, 0); + if (duration == 0) { + duration = 100; + } + delay(duration); + noTone(_pin); + restoreIRTimer(); +#elif defined(ESP8266) + // tone() stops timer 1 + (void) _pin; + (void) frequency; + (void) duration; +#else + tone(_pin, frequency, duration); +#endif +} +#endif // #if defined(USE_IRMP_LIBRARY) diff --git a/examples/IRDispatcherDemo/PinDefinitionsAndMore.h b/examples/IRDispatcherDemo/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/IRDispatcherDemo/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/IRreceiveDemo/IRreceiveDemo.ino b/examples/IRreceiveDemo/IRreceiveDemo.ino deleted file mode 100644 index ac9ebe286..000000000 --- a/examples/IRreceiveDemo/IRreceiveDemo.ino +++ /dev/null @@ -1,51 +0,0 @@ -/* - * IRremote: IRreceiveDemo - demonstrates receiving IR codes with IRrecv - * An IR detector/demodulator must be connected to the input RECV_PIN. - * Initially coded 2009 Ken Shirriff http://www.righto.com/ - */ - -#include - -#if defined(ESP32) -int IR_RECEIVE_PIN = 15; -#elif defined(ARDUINO_AVR_PROMICRO) -int IR_RECEIVE_PIN = 10; -#else -int IR_RECEIVE_PIN = 11; -#endif -IRrecv IrReceiver(IR_RECEIVE_PIN); - -// On the Zero and others we switch explicitly to SerialUSB -#if defined(ARDUINO_ARCH_SAMD) -#define Serial SerialUSB -#endif - -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - - // In case the interrupt driver crashes on setup, give a clue - // to the user what's going on. - Serial.println("Enabling IRin"); - IrReceiver.enableIRIn(); // Start the receiver - IrReceiver.blink13(true); // Enable feedback LED - - Serial.print(F("Ready to receive IR signals at pin ")); - Serial.println(IR_RECEIVE_PIN); -} - -void loop() { - if (IrReceiver.decode()) { - IrReceiver.printResultShort(&Serial); - Serial.println(); - - IrReceiver.resume(); // Receive the next value - } - delay(100); -} diff --git a/examples/IRreceiveDump/IRreceiveDump.ino b/examples/IRreceiveDump/IRreceiveDump.ino deleted file mode 100644 index 2df975b6f..000000000 --- a/examples/IRreceiveDump/IRreceiveDump.ino +++ /dev/null @@ -1,51 +0,0 @@ -/* - * IRremote: IRreceiveDump - dump details of IR codes with IRrecv - * An IR detector/demodulator must be connected to the input RECV_PIN. - * Initially coded 2009 Ken Shirriff http://www.righto.com - * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) - * LG added by Darryl Smith (based on the JVC protocol) - */ - -#include - -/* - * Default is Arduino pin D11. - * You can change this to another available Arduino Pin. - * Your IR receiver should be connected to the pin defined here - */ -#if defined(ESP32) -int IR_RECEIVE_PIN = 15; -#else -int IR_RECEIVE_PIN = 11; -#endif - -IRrecv IrReceiver(IR_RECEIVE_PIN); - -// On the Zero and others we switch explicitly to SerialUSB -#if defined(ARDUINO_ARCH_SAMD) -#define Serial SerialUSB -#endif - -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - IrReceiver.enableIRIn(); // Start the receiver - IrReceiver.blink13(true); // Enable feedback LED - - Serial.print(F("Ready to receive IR signals at pin ")); - Serial.println(IR_RECEIVE_PIN); -} - -void loop() { - if (IrReceiver.decode()) { - Serial.println(); - IrReceiver.printIRResultRaw(&Serial); - IrReceiver.resume(); // Receive the next value - } -} diff --git a/examples/IRreceiveDumpV2/IRreceiveDumpV2.ino b/examples/IRreceiveDumpV2/IRreceiveDumpV2.ino deleted file mode 100644 index 2d321def2..000000000 --- a/examples/IRreceiveDumpV2/IRreceiveDumpV2.ino +++ /dev/null @@ -1,68 +0,0 @@ -//------------------------------------------------------------------------------ -// Include the IRremote library header -// -#include - -//------------------------------------------------------------------------------ -// Tell IRremote which Arduino pin is connected to the IR Receiver (TSOP4838) -// -#if defined(ESP32) -int IR_RECEIVE_PIN = 15; -#else -int IR_RECEIVE_PIN = 11; -#endif -IRrecv IrReceiver(IR_RECEIVE_PIN); - -//+============================================================================= -// Configure the Arduino -// -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(115200); // Status message will be sent to PC at 9600 baud -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - - IrReceiver.enableIRIn(); // Start the receiver - IrReceiver.blink13(true); // Enable feedback LED - - Serial.print(F("Ready to receive IR signals at pin ")); - Serial.println(IR_RECEIVE_PIN); -} - -//+============================================================================= -// Dump out the decode_results structure. -// -void dumpInfo() { - // Check if the buffer overflowed - if (IrReceiver.results.overflow) { - Serial.println("IR code too long. Edit IRremoteInt.h and increase RAW_BUFFER_LENGTH"); - return; - } - - IrReceiver.printResultShort(&Serial); - - Serial.print(" ("); - Serial.print(IrReceiver.results.bits, DEC); - Serial.println(" bits)"); -} - -//+============================================================================= -// The repeating section of the code -// -void loop() { - if (IrReceiver.decode()) { // Grab an IR code - dumpInfo(); // Output the results - IrReceiver.printIRResultRawFormatted(&Serial); // Output the results in RAW format - Serial.println(); // blank line between entries - IrReceiver.printIRResultAsCArray(&Serial); // Output the results as source code array - IrReceiver.printIRResultAsCVariables(&Serial); // Output address and data as source code variables - IrReceiver.printIRResultAsPronto(&Serial); - Serial.println(); // 2 blank lines between entries - Serial.println(); - IrReceiver.resume(); // Prepare for the next value - } -} diff --git a/examples/IRrecord/IRrecord.ino b/examples/IRrecord/IRrecord.ino deleted file mode 100644 index 6e3efaff0..000000000 --- a/examples/IRrecord/IRrecord.ino +++ /dev/null @@ -1,190 +0,0 @@ -/* - * IRrecord: record and play back IR signals as a minimal - * An IR detector/demodulator must be connected to the input RECV_PIN. - * An IR LED must be connected to the output PWM pin 3. - * A button must be connected between the input SEND_BUTTON_PIN and ground. - * A visible LED can be connected to STATUS_PIN to provide status. - * - * The logic is: - * If the button is pressed, send the IR code. - * If an IR code is received, record it. - * - * Initially coded 2009 Ken Shirriff http://www.righto.com - */ - -#include - -#if defined(ESP32) -int IR_RECEIVE_PIN = 15; -int SEND_BUTTON_PIN = 16; // RX2 pin -#else -int IR_RECEIVE_PIN = 11; -int SEND_BUTTON_PIN = 12; -#endif -int STATUS_PIN = LED_BUILTIN; - -int DELAY_BETWEEN_REPEAT = 50; - -IRrecv IrReceiver(IR_RECEIVE_PIN); -IRsend IrSender; - -// On the Zero and others we switch explicitly to SerialUSB -#if defined(ARDUINO_ARCH_SAMD) -#define Serial SerialUSB -#endif - -void setup() { - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - - IrReceiver.enableIRIn(); // Start the receiver - IrReceiver.blink13(true); // Enable feedback LED - - pinMode(SEND_BUTTON_PIN, INPUT_PULLUP); - pinMode(STATUS_PIN, OUTPUT); - - Serial.print(F("Ready to receive IR signals at pin ")); - Serial.println(IR_RECEIVE_PIN); - -#if defined(SENDING_SUPPORTED) - Serial.print(F("Ready to send IR signals at pin ")); - Serial.print(IR_SEND_PIN); - Serial.print(F(" on press of button at pin ")); - Serial.println(SEND_BUTTON_PIN); - -#else - Serial.println(F("Sending not supported for this board!")); -#endif -} - -// Storage for the recorded code -int codeType = -1; // The type of code -uint32_t codeValue; // The code value if not raw -uint16_t address; // The address value if not raw -unsigned int rawCodes[RAW_BUFFER_LENGTH]; // The durations if raw -uint8_t codeLen; // The length of the code -int toggle = 0; // The RC5/6 toggle state - -// Stores the code for later playback -// Most of this code is just logging -void storeCode() { - if (IrReceiver.results.isRepeat) { - Serial.println("Ignore repeat"); - return; - } - codeType = IrReceiver.results.decode_type; - address = IrReceiver.results.address; - -// int count = IrReceiver.results.rawlen; - if (codeType == UNKNOWN) { - Serial.println("Received unknown code, saving as raw"); - codeLen = IrReceiver.results.rawlen - 1; - // To store raw codes: - // Drop first value (gap) - // Convert from ticks to microseconds - // Tweak marks shorter, and spaces longer to cancel out IR receiver distortion - for (int i = 1; i <= codeLen; i++) { - if (i % 2) { - // Mark - rawCodes[i - 1] = IrReceiver.results.rawbuf[i] * MICROS_PER_TICK - MARK_EXCESS_MICROS; - Serial.print(" m"); - } else { - // Space - rawCodes[i - 1] = IrReceiver.results.rawbuf[i] * MICROS_PER_TICK + MARK_EXCESS_MICROS; - Serial.print(" s"); - } - Serial.print(rawCodes[i - 1], DEC); - } - Serial.println(); - } else { - IrReceiver.printResultShort(&Serial); - Serial.println(); - - codeValue = IrReceiver.results.value; - codeLen = IrReceiver.results.bits; - } -} - -void sendCode(bool aSendRepeat) { - if (codeType == NEC) { - if (aSendRepeat) { - IrSender.sendNEC(REPEAT, codeLen); - Serial.println("Sent NEC repeat"); - } else { - IrSender.sendNEC(codeValue, codeLen); - Serial.print("Sent NEC "); - Serial.println(codeValue, HEX); - } - } else if (codeType == NEC_STANDARD) { - if (aSendRepeat) { - IrSender.sendNECRepeat(); - Serial.println("Sent NEC repeat"); - } else { - IrSender.sendNECStandard(address, codeValue); - Serial.print("Sent NEC_STANDARD address=0x"); - Serial.print(address, HEX); - Serial.print(", command=0x"); - Serial.println(codeValue, HEX); - } - } else if (codeType == SONY) { - IrSender.sendSony(codeValue, codeLen); - Serial.print("Sent Sony "); - Serial.println(codeValue, HEX); - } else if (codeType == PANASONIC) { - IrSender.sendPanasonic(codeValue, codeLen); - Serial.print("Sent Panasonic"); - Serial.println(codeValue, HEX); - } else if (codeType == JVC) { - IrSender.sendJVC(codeValue, codeLen, false); - Serial.print("Sent JVC"); - Serial.println(codeValue, HEX); - } else if (codeType == RC5 || codeType == RC6) { - if (!aSendRepeat) { - // Flip the toggle bit for a new button press - toggle = 1 - toggle; - } - // Put the toggle bit into the code to send - codeValue = codeValue & ~(1 << (codeLen - 1)); - codeValue = codeValue | (toggle << (codeLen - 1)); - if (codeType == RC5) { - Serial.print("Sent RC5 "); - Serial.println(codeValue, HEX); - IrSender.sendRC5(codeValue, codeLen); - } else { - IrSender.sendRC6(codeValue, codeLen); - Serial.print("Sent RC6 "); - Serial.println(codeValue, HEX); - } - } else if (codeType == UNKNOWN /* i.e. raw */) { - // Assume 38 KHz - IrSender.sendRaw(rawCodes, codeLen, 38); - Serial.println("Sent raw"); - } -} - -int lastButtonState; - -void loop() { - // If button pressed, send the code. - int buttonState = digitalRead(SEND_BUTTON_PIN); // Button pin is active LOW - if (lastButtonState == LOW && buttonState == HIGH) { - Serial.println("Button released"); - IrReceiver.enableIRIn(); // Re-enable receiver - } - - if (buttonState == LOW) { - Serial.println("Button pressed, now sending"); - digitalWrite(STATUS_PIN, HIGH); - sendCode(lastButtonState == buttonState); - digitalWrite(STATUS_PIN, LOW); - delay(DELAY_BETWEEN_REPEAT); // Wait a bit between retransmissions - } else if (IrReceiver.decode()) { - storeCode(); - IrReceiver.resume(); // resume receiver - } - lastButtonState = buttonState; -} diff --git a/examples/IRrelay/IRrelay.ino b/examples/IRrelay/IRrelay.ino deleted file mode 100644 index 589c880f2..000000000 --- a/examples/IRrelay/IRrelay.ino +++ /dev/null @@ -1,102 +0,0 @@ -/* - * IRremote: IRrelay - demonstrates receiving IR codes with IRrecv - * Toggles an output pin at each command received - * An IR detector/demodulator must be connected to the input RECV_PIN. - * Initially coded 2009 Ken Shirriff http://www.righto.com - */ - -#include - -#if defined(ESP32) -int IR_RECEIVE_PIN = 15; -#else -int IR_RECEIVE_PIN = 11; -#endif -int RELAY_PIN = 4; // is labeled D2 on the Chinese SAMD21 M0-Mini clone - -IRrecv IrReceiver(IR_RECEIVE_PIN); -decode_results results; - -// On the Zero and others we switch explicitly to SerialUSB -#if defined(ARDUINO_ARCH_SAMD) -#define Serial SerialUSB -// The Chinese SAMD21 M0-Mini clone has no led connected, if you connect it, it is on pin 24 like on the original board. -// Attention! D2 and D4 are reversed on these boards -//#undef LED_BUILTIN -//#define LED_BUILTIN 25 // Or choose pin 25, it is the RX pin, but active low. -#endif - -void dump(); - -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - pinMode(RELAY_PIN, OUTPUT); - - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - - IrReceiver.enableIRIn(); // Start the receiver - IrReceiver.blink13(true); // Enable feedback LED - - Serial.print(F("Ready to receive IR signals at pin ")); - Serial.println(IR_RECEIVE_PIN); -} - -int on = 0; -unsigned long last = millis(); - -void loop() { - if (IrReceiver.decode()) { - // If it's been at least 1/4 second since the last - // IR received, toggle the relay - if (millis() - last > 250) { - on = !on; - Serial.print(F("Switch relay ")); - if (on) { - digitalWrite(RELAY_PIN, HIGH); - digitalWrite(LED_BUILTIN, HIGH); - Serial.println(F("on")); - } else { - digitalWrite(RELAY_PIN, LOW); - digitalWrite(LED_BUILTIN, LOW); - Serial.println(F("off")); - } - dump(); - } - last = millis(); - IrReceiver.resume(); // Receive the next value - } -} - - -// Dumps out the decode_results structure. -// Call this after IRrecv::decode() -void dump() { - int count = IrReceiver.results.rawlen; - if (IrReceiver.results.decode_type == UNKNOWN) { - Serial.println("Could not decode message"); - } else { - IrReceiver.printResultShort(&Serial); - - Serial.print(" ("); - Serial.print(IrReceiver.results.bits, DEC); - Serial.println(" bits)"); - } - Serial.print("Raw ("); - Serial.print(count, DEC); - Serial.print("): "); - - for (int i = 0; i < count; i++) { - if ((i % 2) == 1) { - Serial.print(IrReceiver.results.rawbuf[i] * MICROS_PER_TICK, DEC); - } else { - Serial.print(-(int) IrReceiver.results.rawbuf[i] * MICROS_PER_TICK, DEC); - } - Serial.print(" "); - } - Serial.println(""); -} diff --git a/examples/IRremoteExtensionTest/IRremoteExtensionClass.cpp b/examples/IRremoteExtensionTest/IRremoteExtensionClass.cpp new file mode 100644 index 000000000..ef95f8fe0 --- /dev/null +++ b/examples/IRremoteExtensionTest/IRremoteExtensionClass.cpp @@ -0,0 +1,63 @@ +/* + * IRremoteExtensionClass.cpp + * + * Example for a class which itself uses the IRrecv class from IRremote + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2021-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +/* + * !!! The value of RAW_BUFFER_LENGTH (and some other macros) must be the same in all compile units !!! + * Otherwise you may get warnings like "type 'struct IRData' itself violates the C++ One Definition Rule" + */ +#if !defined(RAW_BUFFER_LENGTH) +// For air condition remotes it may require up to 750. Default is 200. +# if (defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF) +#define RAW_BUFFER_LENGTH 360 +# elif (defined(RAMEND) && RAMEND <= 0x8FF) || (defined(RAMSIZE) && RAMSIZE < 0x8FF) +#define RAW_BUFFER_LENGTH 750 +# endif +#endif + +#include "IRremoteExtensionClass.h" + +IRExtensionClass::IRExtensionClass(IRrecv *aIrReceiver) { + MyIrReceiver = aIrReceiver; +} +bool IRExtensionClass::decode() { + return MyIrReceiver->decode(); +} + +bool IRExtensionClass::printIRResultShort(Print *aSerial, bool aCheckForRecordGapsMicros) { + return MyIrReceiver->printIRResultShort(aSerial, aCheckForRecordGapsMicros); +} + +void IRExtensionClass::resume() { + Serial.println(F("Call resume()")); + MyIrReceiver->resume(); +} diff --git a/examples/IRremoteExtensionTest/IRremoteExtensionClass.h b/examples/IRremoteExtensionTest/IRremoteExtensionClass.h new file mode 100644 index 000000000..73e8c5f69 --- /dev/null +++ b/examples/IRremoteExtensionTest/IRremoteExtensionClass.h @@ -0,0 +1,46 @@ +/* + * IRremoteExtensionClass.h + * + * Example for a class which itself uses the IRrecv class from IRremote + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2021-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +#define USE_IRREMOTE_HPP_AS_PLAIN_INCLUDE +#include + +class IRExtensionClass +{ +public: + IRrecv * MyIrReceiver; + IRExtensionClass(IRrecv * aIrReceiver); + bool decode(); + bool printIRResultShort(Print *aSerial, bool aCheckForRecordGapsMicros = true); + void resume(); +}; + diff --git a/examples/IRremoteExtensionTest/IRremoteExtensionTest.ino b/examples/IRremoteExtensionTest/IRremoteExtensionTest.ino new file mode 100644 index 000000000..50bea009f --- /dev/null +++ b/examples/IRremoteExtensionTest/IRremoteExtensionTest.ino @@ -0,0 +1,84 @@ +/* + * IRremoteExtensionTest.cpp + * Simple test using the IRremoteExtensionClass. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2022 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. + +#if !defined(RAW_BUFFER_LENGTH) +// For air condition remotes it may require up to 750. Default is 200. +# if (defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF) +#define RAW_BUFFER_LENGTH 360 +# else +#define RAW_BUFFER_LENGTH 750 +# endif +#endif + +#include + +#include "IRremoteExtensionClass.h" + +/* + * Create the class, which itself uses the IRrecv class from IRremote + */ +IRExtensionClass IRExtension(&IrReceiver); + +void setup() { + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + // Wait until Serial Monitor is attached. + // Required for boards using USB code for Serial like Leonardo. + // Is void for USB Serial implementations using external chips e.g. a CH340. + while (!Serial) + ; + // !!! Program will not proceed if no Serial Monitor is attached !!! +#endif +// Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); + +} + +void loop() { + if (IRExtension.decode()) { + IRExtension.printIRResultShort(&Serial); + IrReceiver.printIRSendUsage(&Serial); + IRExtension.resume(); // Use the extended function provided by IRExtension class + } + delay(100); +} diff --git a/examples/IRremoteExtensionTest/PinDefinitionsAndMore.h b/examples/IRremoteExtensionTest/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/IRremoteExtensionTest/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/IRremoteInfo/IRremoteInfo.ino b/examples/IRremoteInfo/IRremoteInfo.ino index 120ef8dca..25d0f09d9 100644 --- a/examples/IRremoteInfo/IRremoteInfo.ino +++ b/examples/IRremoteInfo/IRremoteInfo.ino @@ -9,8 +9,14 @@ * Version 1.0 November 2015 * Original Author: AnalysIR - IR software & modules for Makers & Pros, visit http://www.AnalysIR.com */ +#include -#include +//#define EXCLUDE_EXOTIC_PROTOCOLS // saves around 240 bytes program memory if IrSender.write is used +//#define SEND_PWM_BY_TIMER +//#define USE_NO_SEND_PWM +//#define NO_LED_FEEDBACK_CODE // saves 566 bytes program memory + +#include // Function declarations for non Arduino IDE's void dumpHeader(); @@ -25,16 +31,22 @@ void dumpArduinoIDE(); void dumpDebugMode(); void dumpProtocols(); void dumpFooter(); -void printSendEnabled(int flag); -void printDecodeEnabled(int flag); void setup() { Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + // Wait until Serial Monitor is attached. + // Required for boards using USB code for Serial like Leonardo. + // Is void for USB Serial implementations using external chips e.g. a CH340. + while (!Serial) + ; + // !!! Program will not proceed if no Serial Monitor is attached !!! #endif + // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); //Runs only once per restart of the Arduino. dumpHeader(); @@ -61,36 +73,36 @@ void dumpRAW_BUFFER_LENGTH() { } void dumpTIMER() { - boolean flag = false; -#ifdef IR_USE_TIMER1 + bool flag = false; +#if defined(IR_USE_TIMER1) Serial.print(F("Timer defined for use: ")); Serial.println(F("Timer1")); flag = true; #endif -#ifdef IR_USE_TIMER2 +#if defined(IR_USE_TIMER2) Serial.print(F("Timer defined for use: ")); Serial.println(F("Timer2")); flag = true; #endif -#ifdef IR_USE_TIMER3 +#if defined(IR_USE_TIMER3) Serial.print(F("Timer defined for use: ")); Serial.println(F("Timer3")); flag = true; #endif -#ifdef IR_USE_TIMER4 +#if defined(IR_USE_TIMER4) Serial.print(F("Timer defined for use: ")); Serial.println(F("Timer4")); flag = true; #endif -#ifdef IR_USE_TIMER5 +#if defined(IR_USE_TIMER5) Serial.print(F("Timer defined for use: ")); Serial.println(F("Timer5")); flag = true; #endif -#ifdef IR_USE_TIMER4_HS +#if defined(IR_USE_TIMER4_HS) Serial.print(F("Timer defined for use: ")); Serial.println(F("Timer4_HS")); flag = true; #endif -#ifdef IR_USE_TIMER_CMT +#if defined(IR_USE_TIMER_CMT) Serial.print(F("Timer defined for use: ")); Serial.println(F("Timer_CMT")); flag = true; #endif -#ifdef IR_USE_TIMER_TPM1 +#if defined(IR_USE_TIMER_TPM1) Serial.print(F("Timer defined for use: ")); Serial.println(F("Timer_TPM1")); flag = true; #endif -#ifdef IR_USE_TIMER_TINY0 +#if defined(IR_USE_TIMER_TINY0) Serial.print(F("Timer defined for use: ")); Serial.println(F("Timer_TINY0")); flag = true; #endif @@ -101,15 +113,19 @@ void dumpTIMER() { } void dumpTimerPin() { -#if defined(SENDING_SUPPORTED) - Serial.print(F("IR Tx Pin: ")); + Serial.print(F("IR Send Pin: ")); +#if defined(IR_SEND_PIN) Serial.println(IR_SEND_PIN); +#else + Serial.println(IrSender.sendPin); #endif } void dumpClock() { +#if defined(F_CPU) Serial.print(F("MCU Clock: ")); Serial.println(F_CPU); +#endif } void dumpPlatform() { @@ -185,13 +201,13 @@ void dumpPulseParams() { ; Serial.println(F(" uSecs")); Serial.print(F("Measurement tolerance: ")); - Serial.print(TOLERANCE); + Serial.print(TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT); Serial.println(F("%")); } void dumpSignalParams() { Serial.print(F("Minimum Gap between IR Signals: ")); - Serial.print(_GAP); + Serial.print(RECORD_GAP_MICROS); Serial.println(F(" uSecs")); } @@ -224,83 +240,98 @@ void dumpProtocols() { Serial.print(F("======== ")); Serial.println(F("========")); Serial.print(F("RC5: ")); - printSendEnabled(SEND_RC5); - printDecodeEnabled(DECODE_RC6); +#if defined(DECODE_RC5) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif Serial.print(F("RC6: ")); - printSendEnabled(SEND_RC6); - printDecodeEnabled(DECODE_RC5); - - Serial.print(F("NEC_STANDARD: ")); - printSendEnabled(SEND_NEC_STANDARD); - printDecodeEnabled(DECODE_NEC_STANDARD); +#if defined(DECODE_RC6) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif Serial.print(F("NEC: ")); - printSendEnabled(SEND_NEC); - printDecodeEnabled(DECODE_NEC); +#if defined(DECODE_NEC) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif Serial.print(F("SONY: ")); - printSendEnabled(SEND_SONY); - printDecodeEnabled(DECODE_SONY); +#if defined(DECODE_SONY) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif Serial.print(F("PANASONIC: ")); - printSendEnabled(SEND_PANASONIC); - printDecodeEnabled(DECODE_PANASONIC); +#if defined(DECODE_PANASONIC) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif Serial.print(F("JVC: ")); - printSendEnabled(SEND_JVC); - printDecodeEnabled(DECODE_JVC); +#if defined(DECODE_JVC) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif Serial.print(F("SAMSUNG: ")); - printSendEnabled(SEND_SAMSUNG); - printDecodeEnabled(DECODE_SAMSUNG); - - Serial.print(F("WHYNTER: ")); - printSendEnabled(SEND_WHYNTER); - printDecodeEnabled(DECODE_WHYNTER); - -// Serial.print(F("AIWA_RC_T501: ")); -// printSendEnabled(SEND_AIWA_RC_T501); -// printDecodeEnabled(DECODE_AIWA_RC_T501); +#if defined(DECODE_SAMSUNG) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif Serial.print(F("LG: ")); - printSendEnabled(SEND_LG); - printDecodeEnabled(DECODE_LG); - - Serial.print(F("SANYO: ")); - printSendEnabled(SEND_SANYO); - printDecodeEnabled(DECODE_SANYO); - -// Serial.print(F("MITSUBISHI: ")); -// printSendEnabled(SEND_MITSUBISHI); -// printDecodeEnabled(DECODE_MITSUBISHI); +#if defined(DECODE_LG) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif - Serial.print(F("DISH: ")); - printSendEnabled(SEND_DISH); - printDecodeEnabled(DECODE_DISH); + Serial.print(F("DENON: ")); +#if defined(DECODE_DENON) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif - Serial.print(F("SHARP: ")); - printSendEnabled(SEND_SHARP); - printDecodeEnabled(DECODE_SHARP); - Serial.print(F("SHARP_ALT: ")); - printSendEnabled(SEND_SHARP_ALT); - printDecodeEnabled(DECODE_SHARP_ALT); +#if !defined(EXCLUDE_EXOTIC_PROTOCOLS) // saves around 2000 bytes program memory - Serial.print(F("DENON: ")); - printSendEnabled(SEND_DENON); - printDecodeEnabled(DECODE_DENON); + Serial.print(F("BANG_OLUFSEN: ")); +#if defined(DECODE_BEO) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif Serial.print(F("BOSEWAVE: ")); - printSendEnabled(SEND_BOSEWAVE); - printDecodeEnabled(DECODE_BOSEWAVE); -} +#if defined(DECODE_BOSEWAVE) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif -void printSendEnabled(int flag) { - if (flag) { - Serial.print(F("Enabled ")); - } else { - Serial.print(F("Disabled ")); - } + Serial.print(F("WHYNTER: ")); +#if defined(DECODE_WHYNTER) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif + + Serial.print(F("FAST: ")); +#if defined(DECODE_FAST) + Serial.println(F("Enabled")); +#else + Serial.println(F("Disabled")); +#endif +#endif } void printDecodeEnabled(int flag) { diff --git a/examples/IRsendDemo/ATtinySerialOut.cpp b/examples/IRsendDemo/ATtinySerialOut.cpp deleted file mode 100644 index b65a611cb..000000000 --- a/examples/IRsendDemo/ATtinySerialOut.cpp +++ /dev/null @@ -1,859 +0,0 @@ -/* - * ATtinySerialOut.cpp - * - * For transmitting debug data over bit bang serial with 115200 baud for 1/8/16 MHz ATtiny clock. - * For 1 MHz you can choose also 38400 baud (120 bytes smaller code size). - * For 8/16 MHz you can choose also 230400 baud (just faster). - * 1 Start, 8 Data, 1 Stop, No Parity - * - * Using PB2 // (Pin7 on Tiny85) as default TX pin to be compatible with digispark board - * To change the output pin, modify the line "#define TX_PIN ..." in TinySerialOut.h or or set it as compiler symbol like "-DTX_PIN PB1". - * - * Using the Serial.print commands needs 4 bytes extra for each call. - * - * - * Copyright (C) 2015-2020 Armin Joachimsmeyer - * Email: armin.joachimsmeyer@gmail.com - * - * This file is part of TinySerialOut https://github.com/ArminJo/ATtinySerialOut. - * - * TinySerialOut is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \ - || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \ - || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) -#include "ATtinySerialOut.h" -#include // for eeprom_read_byte() in writeString_E() - -#ifndef _NOP -#define _NOP() __asm__ volatile ("nop") -#endif - -#ifndef PORTB -#define PORTB (*(volatile uint8_t *)((0x18) + 0x20)) -#endif - -#if defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) -# ifndef TX_PORT -#define TX_PORT PORTA -#define TX_PORT_ADDR 0x02 // PORTA -#define TX_DDR DDRA - -//#define TX_PORT PORTB -//#define TX_PORT_ADDR 0x05 -//#define TX_DDR DDRB -# endif - -#else -// ATtinyX5 here -#define TX_PORT PORTB -#define TX_PORT_ADDR 0x18 // PORTB -#define TX_DDR DDRB -#endif // defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) - -#if defined(Serial) -#undef Serial -#endif - -void write1Start8Data1StopNoParity(uint8_t aValue); - -bool sUseCliSeiForWrite = true; - -void initTXPin() { - // TX_PIN is active LOW, so set it to HIGH initially - TX_PORT |= (1 << TX_PIN); - // set pin direction to output - TX_DDR |= (1 << TX_PIN); -} - -void write1Start8Data1StopNoParityWithCliSei(uint8_t aValue) { - uint8_t oldSREG = SREG; - cli(); - write1Start8Data1StopNoParity(aValue); - SREG = oldSREG; -} - -void writeValue(uint8_t aValue) { - write1Start8Data1StopNoParity(aValue); -} - -/* - * Used for writeString() and therefore all write() and print - */ -void useCliSeiForStrings(bool aUseCliSeiForWrite) { - sUseCliSeiForWrite = aUseCliSeiForWrite; -} - -/* - * Write String residing in RAM - */ -void writeString(const char *aStringPtr) { -#ifndef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT - if (sUseCliSeiForWrite) { -#endif - while (*aStringPtr != 0) { - write1Start8Data1StopNoParityWithCliSei(*aStringPtr++); - } -#ifndef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT - } else { - while (*aStringPtr != 0) { - write1Start8Data1StopNoParity(*aStringPtr++); - } - } -#endif -} - -/* - * Write string residing in program space (FLASH) - */ -void writeString_P(const char *aStringPtr) { - uint8_t tChar = pgm_read_byte((const uint8_t * ) aStringPtr); -// Comparing with 0xFF is safety net for wrong string pointer - while (tChar != 0 && tChar != 0xFF) { -#ifdef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT - write1Start8Data1StopNoParityWithCliSei(tChar); -#else - if (sUseCliSeiForWrite) { - write1Start8Data1StopNoParityWithCliSei(tChar); - } else { - write1Start8Data1StopNoParity(tChar); - } -#endif - tChar = pgm_read_byte((const uint8_t * ) ++aStringPtr); - } -} - -/* - * Write string residing in program space (FLASH) - */ -void writeString(const __FlashStringHelper *aStringPtr) { - PGM_P tPGMStringPtr = reinterpret_cast(aStringPtr); - uint8_t tChar = pgm_read_byte((const uint8_t * ) aStringPtr); -// Comparing with 0xFF is safety net for wrong string pointer - while (tChar != 0 && tChar != 0xFF) { -#ifdef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT - write1Start8Data1StopNoParityWithCliSei(tChar); -#else - if (sUseCliSeiForWrite) { - write1Start8Data1StopNoParityWithCliSei(tChar); - } else { - write1Start8Data1StopNoParity(tChar); - } -#endif - tChar = pgm_read_byte((const uint8_t * ) ++tPGMStringPtr); - } -} - -/* - * Write string residing in EEPROM space - */ -void writeString_E(const char *aStringPtr) { - uint8_t tChar = eeprom_read_byte((const uint8_t *) aStringPtr); - // Comparing with 0xFF is safety net for wrong string pointer - while (tChar != 0 && tChar != 0xFF) { -#ifdef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT - write1Start8Data1StopNoParityWithCliSei(tChar); -#else - if (sUseCliSeiForWrite) { - write1Start8Data1StopNoParityWithCliSei(tChar); - } else { - write1Start8Data1StopNoParity(tChar); - } -#endif - tChar = eeprom_read_byte((const uint8_t *) ++aStringPtr); - } -} - -void writeStringWithoutCliSei(const char *aStringPtr) { - while (*aStringPtr != 0) { - write1Start8Data1StopNoParity(*aStringPtr++); - } -} - -void writeStringWithCliSei(const char *aStringPtr) { - while (*aStringPtr != 0) { - write1Start8Data1StopNoParityWithCliSei(*aStringPtr++); - } -} - -void writeStringSkipLeadingSpaces(const char *aStringPtr) { - // skip leading spaces - while (*aStringPtr == ' ' && *aStringPtr != 0) { - aStringPtr++; - } -#ifndef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT - if (sUseCliSeiForWrite) { -#endif - while (*aStringPtr != 0) { - write1Start8Data1StopNoParityWithCliSei(*aStringPtr++); - } -#ifndef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT - } else { - while (*aStringPtr != 0) { - write1Start8Data1StopNoParity(*aStringPtr++); - } - } -#endif -} - -void writeBinary(uint8_t aByte) { -#ifdef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT - write1Start8Data1StopNoParityWithCliSei(aByte); -#else - if (sUseCliSeiForWrite) { - write1Start8Data1StopNoParityWithCliSei(aByte); - } else { - write1Start8Data1StopNoParity(aByte); - } -#endif -} - -void writeChar(uint8_t aChar) { - writeBinary(aChar); -} - -void writeCRLF() { - writeBinary('\r'); - writeBinary('\n'); -} - -void writeUnsignedByte(uint8_t aByte) { - char tStringBuffer[4]; - utoa(aByte, tStringBuffer, 10); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -/* - * 2 Byte Hex output - */ -void writeUnsignedByteHex(uint8_t aByte) { - char tStringBuffer[3]; - // tStringBuffer[0] = nibbleToHex(aByte >> 4); - // tStringBuffer[1] = nibbleToHex(aByte); - // tStringBuffer[2] = '\0'; - // the utoa() version is 8 bytes smaller than the version with nibbleToHex(), if utoa() is allocated by another function. - utoa(aByte, &tStringBuffer[0], 16); - if (tStringBuffer[1] == '\0') { - tStringBuffer[2] = '\0'; - tStringBuffer[1] = tStringBuffer[0]; - tStringBuffer[0] = '0'; - } - writeString(tStringBuffer); -} - -/* - * 2 Byte Hex output with 2 Byte prefix "0x" - */ -void writeUnsignedByteHexWithPrefix(uint8_t aByte) { - writeBinary('0'); - writeBinary('x'); - writeUnsignedByteHex(aByte); -} - -char nibbleToHex(uint8_t aByte) { - aByte = aByte & 0x0F; - if (aByte < 10) { - return aByte + '0'; - } - return aByte + 'A' - 10; -} - -void writeByte(int8_t aByte) { - char tStringBuffer[5]; - itoa(aByte, tStringBuffer, 10); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void writeInt(int16_t aInteger) { - char tStringBuffer[7]; - itoa(aInteger, tStringBuffer, 10); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void writeUnsignedInt(uint16_t aInteger) { - char tStringBuffer[6]; - utoa(aInteger, tStringBuffer, 10); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void writeLong(int32_t aLong) { - char tStringBuffer[12]; - ltoa(aLong, tStringBuffer, 10); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void writeUnsignedLong(uint32_t aLong) { - char tStringBuffer[11]; - ultoa(aLong, tStringBuffer, 10); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void writeFloat(double aFloat) { - char tStringBuffer[11]; - dtostrf(aFloat, 10, 3, tStringBuffer); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void writeFloat(double aFloat, uint8_t aDigits) { - char tStringBuffer[11]; - dtostrf(aFloat, 10, aDigits, tStringBuffer); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -/****************************************************** - * The TinySerialOut class fuctions which implements - * the Serial + printHex() and printlnHex() functions - ******************************************************/ -/* - * An alternative way to call the init function :-) - */ -void TinySerialOut::begin(long aBaudrate) { - initTXPin(); -#if defined(USE_115200BAUD) // else smaller code, but only 38400 baud at 1 MHz - if (aBaudrate != 115200) { - println(F("Only 115200 supported!")); - } -#else -#if (F_CPU == 1000000) - if (aBaudrate != 38400) { - println(F("Only 38400 supported!")); - } -#else - if (aBaudrate != 230400) { - println(F("Only 230400 supported!")); - } -#endif -#endif -} - -void TinySerialOut::end() { - // no action needed -} - -void TinySerialOut::flush() { - // no action needed, since we do not use a buffer -} - -/* - * 2 Byte Hex output with 2 Byte prefix "0x" - */ -void TinySerialOut::printHex(uint8_t aByte) { - writeUnsignedByteHexWithPrefix(aByte); -} - -void TinySerialOut::printHex(uint16_t aWord) { - writeUnsignedByteHexWithPrefix(aWord >> 8); - writeUnsignedByteHex(aWord); -} - -void TinySerialOut::printlnHex(uint8_t aByte) { - printHex(aByte); - println(); -} - -void TinySerialOut::printlnHex(uint16_t aWord) { - printHex(aWord); - println(); -} - -// virtual functions of Print class -size_t TinySerialOut::write(uint8_t aByte) { - writeBinary(aByte); - return 1; -} - -void TinySerialOut::print(const char* aStringPtr) { - writeString(aStringPtr); -} - -void TinySerialOut::print(const __FlashStringHelper *aStringPtr) { - writeString(aStringPtr); -} - -void TinySerialOut::print(char aChar) { - writeBinary(aChar); -} - -void TinySerialOut::print(uint8_t aByte, uint8_t aBase) { - if (aBase == 16) { - /* - * Print Hex always with two characters - */ - writeUnsignedByteHex(aByte); - } else { - char tStringBuffer[4]; - utoa(aByte, tStringBuffer, aBase); - writeStringSkipLeadingSpaces(tStringBuffer); - } -} - -void TinySerialOut::print(int16_t aInteger, uint8_t aBase) { - char tStringBuffer[7]; - itoa(aInteger, tStringBuffer, aBase); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void TinySerialOut::print(uint16_t aInteger, uint8_t aBase) { - char tStringBuffer[6]; - utoa(aInteger, tStringBuffer, aBase); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void TinySerialOut::print(int32_t aLong, uint8_t aBase) { - char tStringBuffer[12]; - ltoa(aLong, tStringBuffer, aBase); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void TinySerialOut::print(uint32_t aLong, uint8_t aBase) { - char tStringBuffer[11]; - ultoa(aLong, tStringBuffer, aBase); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void TinySerialOut::print(double aFloat, uint8_t aDigits) { - char tStringBuffer[11]; - dtostrf(aFloat, 10, aDigits, tStringBuffer); - writeStringSkipLeadingSpaces(tStringBuffer); -} - -void TinySerialOut::println(char aChar) { - print(aChar); - println(); -} - -void TinySerialOut::println(const char* aStringPtr) { - print(aStringPtr); - println(); -} - -void TinySerialOut::println(const __FlashStringHelper *aStringPtr) { - print(aStringPtr); - println(); -} - -void TinySerialOut::println(uint8_t aByte, uint8_t aBase) { - print(aByte, aBase); - println(); -} - -void TinySerialOut::println(int16_t aInteger, uint8_t aBase) { - print(aInteger, aBase); - println(); -} - -void TinySerialOut::println(uint16_t aInteger, uint8_t aBase) { - print(aInteger, aBase); - println(); -} - -void TinySerialOut::println(int32_t aLong, uint8_t aBase) { - print(aLong, aBase); - println(); -} - -void TinySerialOut::println(uint32_t aLong, uint8_t aBase) { - print(aLong, aBase); - println(); -} - -void TinySerialOut::println(double aFloat, uint8_t aDigits) { - print(aFloat, aDigits); - println(); -} - -void TinySerialOut::println() { - print('\r'); - print('\n'); -} - -/* - * The Serial Instance!!! - */ -// #if ... to be compatible with ATTinyCores and AttinyDigisparkCores -#if (!defined(UBRRH) && !defined(UBRR0H)) /*AttinyDigisparkCore and AttinyDigisparkCore condition*/ \ - || USE_SOFTWARE_SERIAL /*AttinyDigisparkCore condition*/\ - || ((defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(LINBRRH)) && !USE_SOFTWARE_SERIAL)/*AttinyDigisparkCore condition for HardwareSerial*/ -// Switch to SerialOut since Serial is already defined -// or comment out line 745 in TinyDebugSerial.h included in AttinyDigisparkCores/src/tiny/WProgram.h at line 24 for AttinyDigisparkCores -TinySerialOut SerialOut; -#else -TinySerialOut Serial; -#endif - -/******************************** - * Basic serial output function - *******************************/ - -inline void delay4CyclesInlineExact(uint16_t a4Microseconds) { - /* - * The loop takes 4 cycles (4 microseconds at 1 MHz). Last loop is only 3 cycles. Setting of loop counter a4Microseconds needs 2 cycles - * 3 -> 13 cycles (3*4 -1 + 2) = 3*4 + 1 - * 4 -> 17 cycles - * 5 -> 21 cycles - */ - asm volatile ( - "1: sbiw %0,1" "\n\t" // 2 cycles - "brne .-4" : "=w" (a4Microseconds) : "0" (a4Microseconds)// 2 cycles - ); -} - -#if (F_CPU == 1000000) && defined(USE_115200BAUD) //else smaller code, but only 38400 baud at 1 MHz -/* - * 115200 baud - 8,680 cycles per bit, 86,8 per byte at 1 MHz - * - * Assembler code for 115200 baud extracted from Digispark core files: - * Code size is 196 Byte (including first call) - * - * TinySerialOut.h - Tiny write-only software serial. - * Copyright 2010 Rowdy Dog Software. This code is part of Arduino-Tiny. - * - * Arduino-Tiny is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - */ -void write1Start8Data1StopNoParity(uint8_t aValue) { - asm volatile - ( - "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- 0 */ - "ror %[value]" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - - "brcs L%=b0h" "\n\t" /* 1 (not taken) */ - "nop" "\n\t" /* 1 */ - "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- st is 9 cycles */ - "rjmp L%=b0z" "\n\t" /* 2 */ - "L%=b0h: " /* 2 (taken) */ - "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- st is 9 cycles */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "L%=b0z: " - "ror %[value]" "\n\t" /* 1 */ - - "nop" "\n\t" /* 1 */ - - "brcs L%=b1h" "\n\t" /* 1 (not taken) */ - "nop" "\n\t" /* 1 */ - "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b0 is 8 cycles */ - "rjmp L%=b1z" "\n\t" /* 2 */ - "L%=b1h: " /* 2 (taken) */ - "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b0 is 8 cycles */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "L%=b1z: " - "ror %[value]" "\n\t" /* 1 */ - - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - - "brcs L%=b2h" "\n\t" /* 1 (not taken) */ - "nop" "\n\t" /* 1 */ - "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b1 is 9 cycles */ - "rjmp L%=b2z" "\n\t" /* 2 */ - "L%=b2h: " /* 2 (taken) */ - "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b1 is 9 cycles */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "L%=b2z: " - "ror %[value]" "\n\t" /* 1 */ - - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - - "brcs L%=b3h" "\n\t" /* 1 (not taken) */ - "nop" "\n\t" /* 1 */ - "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b2 is 9 cycles */ - "rjmp L%=b3z" "\n\t" /* 2 */ - "L%=b3h: " /* 2 (taken) */ - "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b2 is 9 cycles */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "L%=b3z: " - "ror %[value]" "\n\t" /* 1 */ - - "nop" "\n\t" /* 1 */ - - "brcs L%=b4h" "\n\t" /* 1 (not taken) */ - "nop" "\n\t" /* 1 */ - "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b3 is 8 cycles */ - "rjmp L%=b4z" "\n\t" /* 2 */ - "L%=b4h: " /* 2 (taken) */ - "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b3 is 8 cycles */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "L%=b4z: " - "ror %[value]" "\n\t" /* 1 */ - - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - - "brcs L%=b5h" "\n\t" /* 1 (not taken) */ - "nop" "\n\t" /* 1 */ - "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b4 is 9 cycles */ - "rjmp L%=b5z" "\n\t" /* 2 */ - "L%=b5h: " /* 2 (taken) */ - "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b4 is 9 cycles */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "L%=b5z: " - "ror %[value]" "\n\t" /* 1 */ - - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - - "brcs L%=b6h" "\n\t" /* 1 (not taken) */ - "nop" "\n\t" /* 1 */ - "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b5 is 9 cycles */ - "rjmp L%=b6z" "\n\t" /* 2 */ - "L%=b6h: " /* 2 (taken) */ - "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b5 is 9 cycles */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "L%=b6z: " - "ror %[value]" "\n\t" /* 1 */ - - "nop" "\n\t" /* 1 */ - - "brcs L%=b7h" "\n\t" /* 1 (not taken) */ - "nop" "\n\t" /* 1 */ - "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b6 is 8 cycles */ - "rjmp L%=b7z" "\n\t" /* 2 */ - "L%=b7h: " /* 2 (taken) */ - "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b6 is 8 cycles */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "L%=b7z: " - "nop" "\n\t" /* 1 */ - - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b7 is 9 cycles */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - "nop" "\n\t" /* 1 */ - /* <---sp is 9 cycles */ - - : - : - [value] "r" ( aValue ), - [txport] "I" ( TX_PORT_ADDR ), - [txpin] "I" ( TX_PIN ) - ); -} -#else -/* - * Small code using loop. Code size is 76 Byte (including first call) - * - * 1 MHz CPU Clock - * 26,04 cycles per bit, 260,4 per byte for 38400 baud at 1 MHz Clock - * 17,36 cycles per bit, 173,6 per byte for 57600 baud at 1 MHz Clock -> therefore use 38400 baud - * 24 cycles between each cbi/sbi (Clear/Set Bit in IO-register) command. - * - * 8 MHz CPU Clock - * 69,44 cycles per bit, 694,4 per byte for 115200 baud at 8 MHz Clock - * 34,72 cycles per bit, 347,2 per byte for 230400 baud at 8 MHz Clock. - * 68 / 33 cycles between each cbi (Clear Bit in IO-register) or sbi command. - * - * 16 MHz CPU Clock - * 138,88 cycles per bit, 1388,8 per byte for 115200 baud at 16 MHz Clock - * 69,44 cycles per bit, 694,4 per byte for 230400 baud at 16 MHz Clock - * 137 / 68 cycles between each cbi (Clear Bit in IO-register) or sbi command. - * - * 2 cycles for each cbi/sbi instruction. - */ -void write1Start8Data1StopNoParity(uint8_t aValue) { - asm volatile - ( - "cbi %[txport] , %[txpin]" "\n\t" // 2 PORTB &= ~(1 << TX_PIN); -#if (F_CPU == 1000000) && !defined(USE_115200BAUD) // 1 MHz 38400 baud - // 0 cycles padding to get additional 4 cycles - //delay4CyclesInlineExact(5); -> 20 cycles - "ldi r30 , 0x05" "\n\t"// 1 -#elif ((F_CPU == 8000000) && defined(USE_115200BAUD)) || ((F_CPU == 16000000) && !defined(USE_115200BAUD)) // 8 MHz 115200 baud OR 16 MHz 230400 baud - // 3 cycles padding to get additional 7 cycles - "nop" "\n\t"// 1 _nop"(); - "nop" "\n\t"// 1 _nop"(); - "nop" "\n\t"// 1 _nop"(); - //delay4CyclesInlineExact(15); -> 61 cycles - "ldi r30 , 0x0F" "\n\t"// 1 -#elif (F_CPU == 8000000) && !defined(USE_115200BAUD) // 8 MHz 230400 baud - // 0 cycles padding to get additional 4 cycles - //delay4CyclesInlineExact(7); -> 29 cycles - "ldi r30 , 0x07" "\n\t"// 1 -#elif (F_CPU == 16000000) && defined(USE_115200BAUD) // 16 MHz 115200 baud - // 0 cycles padding to get additional 4 cycles - //delay4CyclesInlineExact(33); -> 133 cycles - "ldi r30 , 0x21" "\n\t"// 1 -#endif - "ldi r31 , 0x00" "\n\t" // 1 - "delay1:" - "sbiw r30 , 0x01" "\n\t"// 2 - "brne delay1" "\n\t"// 1-2 - - "ldi r25 , 0x08" "\n\t"// 1 - - // Start of loop - // if (aValue & 0x01) { - "txloop:" - "sbrs %[value] , 0" "\n\t"// 1 - "rjmp .+6" "\n\t"// 2 - - "nop" "\n\t"// 1 - "sbi %[txport] , %[txpin]" "\n\t"// 2 PORTB |= 1 << TX_PIN; - "rjmp .+6" "\n\t"// 2 - - "cbi %[txport] , %[txpin]" "\n\t"// 2 PORTB &= ~(1 << TX_PIN); - "nop" "\n\t"// 1 - "nop" "\n\t"// 1 - "lsr %[value]" "\n\t"// 1 aValue = aValue >> 1; - -#if (F_CPU == 1000000) && !defined(USE_115200BAUD) // 1 MHz 38400 baud - // 3 cycles padding to get additional 11 cycles - "nop" "\n\t"// 1 - "nop" "\n\t"// 1 - "nop" "\n\t"// 1 - // delay4CyclesInlineExact(3); -> 13 cycles - "ldi r30 , 0x03" "\n\t"// 1 -#elif ((F_CPU == 8000000) && defined(USE_115200BAUD)) || ((F_CPU == 16000000) && !defined(USE_115200BAUD)) // 8 MHz 115200 baud OR 16 MHz 230400 baud - // 3 cycles padding to get additional 11 cycles - "nop" "\n\t"// 1 - "nop" "\n\t"// 1 - "nop" "\n\t"// 1 - // delay4CyclesInlineExact(14); -> 57 cycles - "ldi r30 , 0x0E" "\n\t"// 1 -#elif (F_CPU == 8000000) && !defined(USE_115200BAUD) // 8 MHz 230400 baud - // 0 cycles padding to get additional 8 cycles - // delay4CyclesInlineExact(6); -> 25 cycles - "ldi r30 , 0x05" "\n\t"// 1 -#elif (F_CPU == 16000000) && defined(USE_115200BAUD) // 16 MHz 115200 baud - // 0 cycles padding to get additional 8 cycles - //delay4CyclesInlineExact(32); -> 129 cycles - "ldi r30 , 0x20" "\n\t"// 1 -#endif - "ldi r31 , 0x00" "\n\t" // 1 - "delay2:" - "sbiw r30 , 0x01" "\n\t"// 2 - "brne delay2" "\n\t"// 1-2 - - // }while (i > 0); - "subi r25 , 0x01" "\n\t"// 1 - "brne txloop" "\n\t"// 1-2 - // To compensate for missing loop cycles at last bit - "nop" "\n\t"// 1 - "nop" "\n\t"// 1 - "nop" "\n\t"// 1 - "nop" "\n\t"// 1 - - // Stop bit - "sbi %[txport] , %[txpin]" "\n\t"// 2 PORTB |= 1 << TX_PIN; - -#if (F_CPU == 1000000) && !defined(USE_115200BAUD) // 1 MHz 38400 baud - // delay4CyclesInlineExact(4); -> 17 cycles - gives minimum 25 cycles for stop bit - "ldi r30 , 0x04" "\n\t"// 1 -#elif ((F_CPU == 8000000) && defined(USE_115200BAUD)) || ((F_CPU == 16000000) && !defined(USE_115200BAUD)) // 8 MHz 115200 baud OR 16 MHz 230400 baud - // delay4CyclesInlineExact(15) -> 61 cycles - gives minimum 69 cycles for stop bit - "ldi r30 , 0x0F" "\n\t"// 1 -#elif (F_CPU == 8000000) && !defined(USE_115200BAUD) // 8 MHz 230400 baud - // delay4CyclesInlineExact(5) -> 27 cycles - gives minimum 35 cycles for stop bit - "ldi r30 , 0x05" "\n\t"// 1 -#elif (F_CPU == 16000000) && defined(USE_115200BAUD) // 16 MHz 115200 baud - // delay4CyclesInlineExact(32) -> 129 cycles - gives minimum 137 cycles for stop bit - "ldi r30 , 0x20" "\n\t"// 1 -#endif - "ldi r31 , 0x00" "\n\t" // 1 - "delay3:" - "sbiw r30 , 0x01" "\n\t"// - "brne delay3" "\n\t"// 1-2 - // return needs 4 cycles, load of next value needs 1 cycle, next rcall needs 3 cycles -> gives additional 8 cycles minimum for stop bit - - : - : - [value] "r" ( aValue ), - [txport] "I" ( TX_PORT_ADDR ) , /* 0x18 is PORTB on Attiny 85 */ - [txpin] "I" ( TX_PIN ) - : - "r25", - "r30", - "r31" - ); - -} -#endif - -/* - * C Version which generates the assembler code above. - * In order to guarantee the correct timing, compile with Arduino standard settings or: - * avr-g++ -I"C:\arduino\hardware\arduino\avr\cores\arduino" -I"C:\arduino\hardware\arduino\avr\variants\standard" -c -g -w -Os -ffunction-sections -fdata-sections -mmcu=attiny85 -DF_CPU=1000000UL -MMD -o "TinySerialOut.o" "TinySerialOut.cpp" - * Tested with Arduino 1.6.8 and 1.8.5/gcc4.9.2 - * C Version does not work with AVR gcc7.3.0, since optimization is too bad - */ -void write1Start8Data1StopNoParity_C_Version(uint8_t aValue) { - /* - * C Version here for 38400 baud at 1 MHz Clock. You see, it is simple :-) - */ -// start bit - TX_PORT &= ~(1 << TX_PIN); - _NOP(); - delay4CyclesInlineExact(4); - -// 8 data bits - uint8_t i = 8; - do { - if (aValue & 0x01) { - // bit=1 - // to compensate for jump at data=0 - _NOP(); - TX_PORT |= 1 << TX_PIN; - } else { - // bit=0 - TX_PORT &= ~(1 << TX_PIN); - // compensate for different cycles of sbrs - _NOP(); - _NOP(); - } - aValue = aValue >> 1; - // 3 cycles padding - _NOP(); - _NOP(); - _NOP(); - delay4CyclesInlineExact(3); - --i; - } while (i > 0); - -// to compensate for missing loop cycles at last bit - _NOP(); - _NOP(); - _NOP(); - _NOP(); - -// Stop bit - TX_PORT |= 1 << TX_PIN; -// -8 cycles to compensate for fastest repeated call (1 ret + 1 load + 1 call) - delay4CyclesInlineExact(4); // gives minimum 25 cycles for stop bit :-) -} -#endif // defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) - diff --git a/examples/IRsendDemo/ATtinySerialOut.h b/examples/IRsendDemo/ATtinySerialOut.h deleted file mode 100644 index b1c65276d..000000000 --- a/examples/IRsendDemo/ATtinySerialOut.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * ATtinySerialOut.h - * - * Copyright (C) 2015-2020 Armin Joachimsmeyer - * Email: armin.joachimsmeyer@gmail.com - * - * This file is part of TinySerialOut https://github.com/ArminJo/ATtinySerialOut. - * - * TinySerialOut is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -// -// ATMEL ATTINY85 -// -// +-\/-+ -// PCINT5/!RESET/ADC0/dW (5) PB5 1| |8 VCC -// USB+ PCINT3/XTAL1/CLKI/!OC1B/ADC3 (3) PB3 2| |7 PB2 (2) SCK/USCK/SCL/ADC1/T0/INT0/PCINT2 / TX Debug output -// USB- PCINT4/XTAL2/CLKO/ OC1B/ADC2 (4) PB4 3| |6 PB1 (1) MISO/DO/AIN1/OC0B/ OC1A/PCINT1 - (Digispark) LED -// GND 4| |5 PB0 (0) MOSI/DI/AIN0/OC0A/!OC1A/SDA/AREF/PCINT0 -// +----+ - -// ATMEL ATTINY167 -// Pin numbers are for Digispark core -// Pin numbers in Parenthesis are for ATTinyCore -// -// +-\/-+ -// RX 6 (0) PA0 1| |20 PB0 (D8) 0 OC1AU TONE Timer 1 Channel A -// TX 7 (1) PA1 2| |19 PB1 (9) 1 OC1BU Internal LED -// 8 (2) PA2 3| |18 PB2 (10) 2 OC1AV Timer 1 Channel B -// INT1 9 (3) PA3 4| |17 PB3 (11) 4 OC1BV connected with 51 Ohm to D- and 3.3 volt Zener. -// AVCC 5| |16 GND -// AGND 6| |15 VCC -// 10 (4) PA4 7| |14 PB4 (12) XTAL1 -// 11 (5) PA5 8| |13 PB5 (13) XTAL2 -// 12 (6) PA6 9| |12 PB6 (14) 3 INT0 connected with 68 Ohm to D+ (and disconnected 3.3 volt Zener). Is terminated with ~20 kOhm if USB attached :-( -// 5 (7) PA7 10| |11 PB7 (15) RESET -// +----+ -// - -#ifndef ATTINY_SERIAL_OUT_H_ -#define ATTINY_SERIAL_OUT_H_ - -#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \ - || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \ - || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) -#include - -#define VERSION_ATTINY_SERIAL_OUT "1.2.1" -#define VERSION_ATTINY_SERIAL_OUT_MAJOR 1 -#define VERSION_ATTINY_SERIAL_OUT_MINOR 2 - -#if (F_CPU != 1000000) && (F_CPU != 8000000) && (F_CPU != 16000000) -#error F_CPU value must be 1000000, 8000000 or 16000000. -#endif - -#if defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) -# ifndef TX_PIN -#define TX_PIN PA1 // (package pin 2 / TXD on Tiny167) - can use one of PA0 to PA7 here -# endif -#else // defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) -# ifndef TX_PIN -#define TX_PIN PB2 // (package pin 7 on Tiny85) - can use one of PB0 to PB4 (+PB5) here -# endif -#endif - -/* - * Define or comment this out, if you want to save 10 bytes code size and if you can live - * with 87 micro seconds intervals of disabled interrupts for each sent byte @115200 baud. - */ -//#define USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT - -/* - * @1 MHz use bigger (+120 bytes for unrolled loop) but faster code. Otherwise only 38400 baud is possible. - * @8/16 MHz use 115200 baud instead of 230400 baud. - */ -//#define TINY_SERIAL_DO_NOT_USE_115200BAUD -#ifndef TINY_SERIAL_DO_NOT_USE_115200BAUD // define this to force using other baud rates -#define USE_115200BAUD -#endif - -// The same class definition as for plain arduino -#if not defined(F) -class __FlashStringHelper; -#define F(string_literal) (reinterpret_cast(PSTR(string_literal))) -#endif - -extern bool sUseCliSeiForWrite; // default is true -void useCliSeiForStrings(bool aUseCliSeiForWrite); // might be useful to set to false if output is done from ISR, to avoid to call unwanted sei(). - -void initTXPin(); -void write1Start8Data1StopNoParity(uint8_t aValue); -void write1Start8Data1StopNoParityWithCliSei(uint8_t aValue); -void writeValue(uint8_t aValue); - -void writeString(const char *aStringPtr); -void writeString(const __FlashStringHelper *aStringPtr); -void writeString_P(const char *aStringPtr); -void writeString_E(const char *aStringPtr); -void writeStringWithCliSei(const char *aStringPtr); -void writeStringWithoutCliSei(const char *aStringPtr); -void writeStringSkipLeadingSpaces(const char *aStringPtr); - -void writeBinary(uint8_t aByte); // write direct without decoding -void writeChar(uint8_t aChar); // Synonym for writeBinary -void writeCRLF(); -void writeByte(int8_t aByte); -void writeUnsignedByte(uint8_t aByte); -void writeUnsignedByteHex(uint8_t aByte); -void writeUnsignedByteHexWithPrefix(uint8_t aByte); -void writeInt(int16_t aInteger); -void writeUnsignedInt(uint16_t aInteger); -void writeLong(int32_t aLong); -void writeUnsignedLong(uint32_t aLong); -void writeFloat(double aFloat); -void writeFloat(double aFloat, uint8_t aDigits); - -char nibbleToHex(uint8_t aByte); - -class TinySerialOut -{ -public: - - void begin(long); - void end(); - void flush(void); - - void printHex(uint8_t aByte); // with 0x prefix - void printHex(uint16_t aWord); // with 0x prefix - void printlnHex(uint8_t aByte); // with 0x prefix - void printlnHex(uint16_t aWord); // with 0x prefix - - // virtual functions of Print class - size_t write(uint8_t aByte); - operator bool(); // To support "while (!Serial); // wait for serial port to connect. Needed for Leonardo only - - void print(const __FlashStringHelper *aStringPtr); - void print(const char* aStringPtr); - void print(char aChar); - void print(uint8_t aByte, uint8_t aBase = 10); - void print(int16_t aInteger, uint8_t aBase = 10); - void print(uint16_t aInteger, uint8_t aBase = 10); - void print(int32_t aLong, uint8_t aBase = 10); - void print(uint32_t aLong, uint8_t aBase = 10); - void print(double aFloat, uint8_t aDigits = 2); - - void println(const char* aStringPtr); - void println(const __FlashStringHelper *aStringPtr); - void println(char aChar); - void println(uint8_t aByte, uint8_t aBase = 10); - void println(int16_t aInteger, uint8_t aBase = 10); - void println(uint16_t aInteger, uint8_t aBase = 10); - void println(int32_t aLong, uint8_t aBase = 10); - void println(uint32_t aLong, uint8_t aBase = 10); - void println(double aFloat, uint8_t aDigits = 2); - - void println(void); - -}; - -// #if ... to be compatible with ATTinyCores and AttinyDigisparkCores -#if (!defined(UBRRH) && !defined(UBRR0H)) /*AttinyDigisparkCore and AttinyDigisparkCore condition*/ \ - || USE_SOFTWARE_SERIAL /*AttinyDigisparkCore condition*/\ - || ((defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(LINBRRH)) && !USE_SOFTWARE_SERIAL)/*AttinyDigisparkCore condition for HardwareSerial*/ -// Switch to SerialOut since Serial is already defined -// or comment out line 745 in TinyDebugSerial.h included in AttinyDigisparkCores/src/tiny/WProgram.h at line 24 for AttinyDigisparkCores -extern TinySerialOut SerialOut; -#define Serial SerialOut -#else -extern TinySerialOut Serial; -#endif -#define Print TinySerialOut - -#endif // defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) - -#endif /* ATTINY_SERIAL_OUT_H_ */ - -#pragma once diff --git a/examples/IRsendDemo/IRsendDemo.ino b/examples/IRsendDemo/IRsendDemo.ino deleted file mode 100644 index 185d122ec..000000000 --- a/examples/IRsendDemo/IRsendDemo.ino +++ /dev/null @@ -1,56 +0,0 @@ -/* - * IRremote: IRsendDemo - demonstrates sending IR codes with IRsend - * An IR LED must be connected to Arduino PWM pin 3. - * Initially coded 2009 Ken Shirriff http://www.righto.com - */ - -#include -#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) -#include "ATtinySerialOut.h" -#endif - -IRsend IrSender; - -// On the Zero and others we switch explicitly to SerialUSB -#if defined(ARDUINO_ARCH_SAMD) -#define Serial SerialUSB -#endif - -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - Serial.print(F("Ready to send IR signals at pin ")); - Serial.println(IR_SEND_PIN); -} - -void loop() { -#ifdef SEND_NEC_STANDARD - static uint8_t sCommand = 9; - IrSender.sendNECStandard(0xFF00, sCommand, 2); - Serial.println(F("sendNECStandard(0xFF00, sCommand,2)")); - sCommand++; -#else - unsigned long tData = 0xa90; - // loop for repeats - for (int i = 0; i < 3; i++) { - IrSender.sendSony(tData, 12); - Serial.print(F("sendSony(0x")); - Serial.print(tData,HEX); - Serial.println(F(", 12)")); -// IrSender.sendJVC(0xC5B8, 16,0); // hex value, 16 bits, no repeat -// delayMicroseconds(50); // see http://www.sbprojects.com/knowledge/ir/jvc.php for information -// IrSender.sendJVC(0xC5B8, 16,1); // hex value, 16 bits, repeat -// Serial.println(F("sendJVC(9xC5B8, 16)")); - - delay(40); - } - tData++; -#endif - delay(5000); //5 second delay between each signal burst -} diff --git a/examples/IRsendNecStandardDemo/IRsendNecStandardDemo.ino b/examples/IRsendNecStandardDemo/IRsendNecStandardDemo.ino deleted file mode 100644 index 6b69d25ca..000000000 --- a/examples/IRsendNecStandardDemo/IRsendNecStandardDemo.ino +++ /dev/null @@ -1,62 +0,0 @@ -/* - * IRremote: IRsendNecStandardDemo - * - * Demonstrates sending NEC IR codes in standard format with 16 bit Address 8bit Data - * An IR LED must be connected to Arduino PWM pin 3 (IR_SEND_PIN). - * To receive IR signals in compatible format, you must comment out the line - * #define USE_NEC_STANDARD in IRremote.h. - * - * This file is part of Arduino-IRremote https://github.com/z3t0/Arduino-IRremote. - */ - -#include - -IRsend IrSender; - -// On the Zero and others we switch explicitly to SerialUSB -#if defined(ARDUINO_ARCH_SAMD) -#define Serial SerialUSB -#endif - -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - Serial.print(F("Ready to send IR signals at pin ")); - Serial.println(IR_SEND_PIN); -} - -uint16_t sAddress = 0xa90; -uint8_t sCommand = 0x34; -uint8_t sRepeats = 0; - -void loop() { - - IrSender.sendNECStandard(sAddress, sCommand, sRepeats); - /* - * Print values - */ - Serial.print(F("Send NEC standard: address=0x")); - Serial.print(sAddress, HEX); - Serial.print(F(" command=0x")); - Serial.print(sCommand, HEX); - Serial.print(F(" repeats=")); - Serial.print(sRepeats); - Serial.println(); - /* - * Increment values - */ - sAddress++; - sCommand++; - sRepeats++; - if (sRepeats > 5) { - sRepeats = 5; - } - - delay(2000); // 2 second delay between each signal -} diff --git a/examples/IRsendProntoDemo/IRsendProntoDemo.ino b/examples/IRsendProntoDemo/IRsendProntoDemo.ino deleted file mode 100644 index 5905613aa..000000000 --- a/examples/IRsendProntoDemo/IRsendProntoDemo.ino +++ /dev/null @@ -1,51 +0,0 @@ -// Define exactly one of these -//#define VAR_IN_PROGMEM -#define VAR_IN_MEM -//#define USE_F_FORM - -#define TIMES_TO_SEND 10U - -#include - -const char yamahaVolDown[] -#ifdef VAR_IN_PROGMEM - PROGMEM -#endif - = "0000 006C 0022 0002 " - "015B 00AD 0016 0016 0016 0041 0016 0016 0016 0041 0016 0041 0016 0041 0016 0041 0016 0016 0016 0041 0016 0016 0016 0041 " - "0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0041 0016 0041 0016 0016 0016 0041 0016 0041 0016 0016 0016 0016 " - "0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0041 0016 0041 0016 0041 0016 05F7 015B 0057 0016 0E6C"; - -IRsend irsend; - -void setup() { - Serial.begin(115200); - while (!Serial) - ; - - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - - Serial.print(F("Will send IR signals at pin ")); - Serial.println(IR_SEND_PIN); -} - -void loop() { - - -#ifdef VAR_IN_PROGMEM - Serial.println(F("Sending from PROGMEM")); - irsend.sendPronto_PF(yamahaVolDown, TIMES_TO_SEND); -#elif defined(VAR_IN_MEM) - Serial.println(F("Sending from normal memory")); - irsend.sendPronto(yamahaVolDown, TIMES_TO_SEND); -#else - Serial.println(F("Sending using the F()-form")); - irsend.sendPronto(F("0000 006C 0022 0002 " - "015B 00AD 0016 0016 0016 0041 0016 0016 0016 0041 0016 0041 0016 0041 0016 0041 0016 0016 0016 0041 0016 0016 0016 0041 " - "0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0041 0016 0041 0016 0016 0016 0041 0016 0041 0016 0016 0016 0016 " - "0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0041 0016 0041 0016 0041 0016 05F7 015B 0057 0016 0E6C"), TIMES_TO_SEND); -#endif - - delay(5000); -} diff --git a/examples/IRsendRawDemo/IRsendRawDemo.ino b/examples/IRsendRawDemo/IRsendRawDemo.ino deleted file mode 100644 index 257463806..000000000 --- a/examples/IRsendRawDemo/IRsendRawDemo.ino +++ /dev/null @@ -1,62 +0,0 @@ -/* - * IRremote: IRsendRawDemo - demonstrates sending IR codes with sendRaw - * An IR LED must be connected to Arduino PWM pin 3. - * Initially coded 2009 Ken Shirriff http://www.righto.com - * - * IRsendRawDemo - added by AnalysIR (via www.AnalysIR.com), 24 August 2015 - * - * This example shows how to send a RAW signal using the IRremote library. - * The example signal is actually a 32 bit NEC signal. - * Remote Control button: LGTV Power On/Off. - * Hex Value: 0x20DF10EF, 32 bits - * - * It is more efficient to use the sendNEC function to send NEC signals. - * Use of sendRaw here, serves only as an example of using the function. - * - */ - -#include - -IRsend IrSender; - -// On the Zero and others we switch explicitly to SerialUSB -#if defined(ARDUINO_ARCH_SAMD) -#define Serial SerialUSB -#endif - -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - Serial.print(F("Ready to send IR signals at pin ")); - Serial.println(IR_SEND_PIN); -} - -void loop() { - int khz = 38; // 38kHz carrier frequency for the NEC protocol - /* - * Send data from RAM - */ - unsigned int irSignal[] = { 9000, 4500, 560, 560, 560, 560, 560, 1690, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, - 1690, 560, 1690, 560, 560, 560, 1690, 560, 1690, 560, 1690, 560, 1690, 560, 1690, 560, 560, 560, 560, 560, 560, 560, - 1690, 560, 560, 560, 560, 560, 560, 560, 560, 560, 1690, 560, 1690, 560, 1690, 560, 560, 560, 1690, 560, 1690, 560, - 1690, 560, 1690, 560, 39416, 9000, 2210, 560 }; // AnalysIR Batch Export (IRremote) - RAW - IrSender.sendRaw(irSignal, sizeof(irSignal) / sizeof(irSignal[0]), khz); // Note the approach used to automatically calculate the size of the array. - - delay(2000); - /* - * Send data direct from FLASH - */ - unsigned int irSignalP[] PROGMEM = { 9000, 4500, 560, 560, 560, 560, 560, 1690, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, - 560, 1690, 560, 1690, 560, 560, 560, 1690, 560, 1690, 560, 1690, 560, 1690, 560, 1690, 560, 560, 560, 560, 560, 560, - 560, 1690, 560, 560, 560, 560, 560, 560, 560, 560, 560, 1690, 560, 1690, 560, 1690, 560, 560, 560, 1690, 560, 1690, 560, - 1690, 560, 1690, 560, 39416, 9000, 2210, 560 }; // AnalysIR Batch Export (IRremote) - RAW - IrSender.sendRaw_P(irSignalP, sizeof(irSignalP) / sizeof(irSignalP[0]), khz); // Note the approach used to automatically calculate the size of the array. - - delay(5000); //In this example, the signal will be repeated every 7 seconds, approximately. -} diff --git a/examples/IRtest/IRtest.ino b/examples/IRtest/IRtest.ino deleted file mode 100644 index 025438e05..000000000 --- a/examples/IRtest/IRtest.ino +++ /dev/null @@ -1,169 +0,0 @@ -/* - * IRremote: IRtest unittest - * Initially coded 2009 Ken Shirriff http://www.righto.com - * - * Note: to run these tests, edit IRremote/IRremote.h to add "#define TEST" - * You must then recompile the library by removing IRremote.o and restarting - * the arduino IDE. - */ - -#include - -IRrecv IrReceiver(0); - -// Dumps out the decode_results structure. -// Call this after IRrecv::decode() -// void * to work around compiler issue -//void dump(void *v) { -// decode_results *results = (decode_results *)v -void dump() { - int count = IrReceiver.results.rawlen; - - if (IrReceiver.results.decode_type == UNKNOWN) { - Serial.println("Could not decode message"); - } else { - IrReceiver.printResultShort(&Serial); - - Serial.print(" ("); - Serial.print(IrReceiver.results.bits, DEC); - Serial.println(" bits)"); - } - Serial.print("Raw ("); - Serial.print(count, DEC); - Serial.print("): "); - - for (int i = 0; i < count; i++) { - if ((i % 2) == 1) { - Serial.print(IrReceiver.results.rawbuf[i] * MICROS_PER_TICK, DEC); - } else { - Serial.print(-(int) IrReceiver.results.rawbuf[i] * MICROS_PER_TICK, DEC); - } - Serial.print(" "); - } - Serial.println(""); -} - -class IRsendDummy: public IRsend { -public: - // For testing, just log the marks/spaces -#define SENDLOG_LEN 128 - int sendlog[SENDLOG_LEN]; - int sendlogcnt = 0; - IRsendDummy() : - IRsend() { - } - void reset() { - sendlogcnt = 0; - } - void mark(int time) { - sendlog[sendlogcnt] = time; - if (sendlogcnt < SENDLOG_LEN) - sendlogcnt++; - } - void space(int time) { - sendlog[sendlogcnt] = -time; - if (sendlogcnt < SENDLOG_LEN) - sendlogcnt++; - } - // Copies the dummy buf into the interrupt buf - void useDummyBuf() { - int last = SPACE; - irparams.rcvstate = IR_REC_STATE_STOP; - irparams.rawlen = 1; // Skip the gap - for (int i = 0; i < sendlogcnt; i++) { - if (sendlog[i] < 0) { - if (last == MARK) { - // New space - irparams.rawbuf[irparams.rawlen++] = (-sendlog[i] - MARK_EXCESS_MICROS) / MICROS_PER_TICK; - last = SPACE; - } else { - // More space - irparams.rawbuf[irparams.rawlen - 1] += -sendlog[i] / MICROS_PER_TICK; - } - } else if (sendlog[i] > 0) { - if (last == SPACE) { - // New mark - irparams.rawbuf[irparams.rawlen++] = (sendlog[i] + MARK_EXCESS_MICROS) / MICROS_PER_TICK; - last = MARK; - } else { - // More mark - irparams.rawbuf[irparams.rawlen - 1] += sendlog[i] / MICROS_PER_TICK; - } - } - } - if (irparams.rawlen % 2) { - irparams.rawlen--; // Remove trailing space - } - } -}; - -IRsendDummy irsenddummy; - -void verify(unsigned long val, int bits, int type) { - irsenddummy.useDummyBuf(); - IrReceiver.decode(); - Serial.print("Testing "); - Serial.print(val, HEX); - if (IrReceiver.results.value == val && IrReceiver.results.bits == bits && IrReceiver.results.decode_type == type) { - Serial.println(": OK"); - } else { - Serial.println(": Error"); - dump(); - } -} - -void testNEC(uint32_t val, int bits) { - irsenddummy.reset(); - irsenddummy.sendNEC(val, bits); - verify(val, bits, NEC); -} -void testSony(unsigned long val, int bits) { - irsenddummy.reset(); - irsenddummy.sendSony(val, bits); - verify(val, bits, SONY); -} -void testRC5(uint32_t val, int bits) { - irsenddummy.reset(); - irsenddummy.sendRC5(val, bits); - verify(val, bits, RC5); -} -void testRC6(uint32_t val, int bits) { - irsenddummy.reset(); - irsenddummy.sendRC6(val, bits); - verify(val, bits, RC6); -} - -void test() { - Serial.println("NEC tests"); - testNEC(0x00000000, 32); - testNEC(0xffffffff, 32); - testNEC(0xaaaaaaaa, 32); - testNEC(0x55555555, 32); - testNEC(0x12345678, 32); - Serial.println("Sony tests"); - testSony(0xfff, 12); - testSony(0x000, 12); - testSony(0xaaa, 12); - testSony(0x555, 12); - testSony(0x123, 12); - Serial.println("RC5 tests"); - testRC5(0xfff, 12); - testRC5(0x000, 12); - testRC5(0xaaa, 12); - testRC5(0x555, 12); - testRC5(0x123, 12); - Serial.println("RC6 tests"); - testRC6(0xfffff, 20); - testRC6(0x00000, 20); - testRC6(0xaaaaa, 20); - testRC6(0x55555, 20); - testRC6(0x12345, 20); -} - -void setup() { - Serial.begin(115200); - test(); -} - -void loop() { -} diff --git a/examples/IRtest2/IRtest2.ino b/examples/IRtest2/IRtest2.ino deleted file mode 100644 index 5f16178a6..000000000 --- a/examples/IRtest2/IRtest2.ino +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Test send/receive functions of IRremote, using a pair of Arduinos. - * - * Arduino #1 should have an IR LED connected to the send pin (3). - * Arduino #2 should have an IR detector/demodulator connected to the - * receive pin (11) and a visible LED connected to pin 3. - * - * The cycle: - * Arduino #1 will wait 2 seconds, then run through the tests. - * It repeats this forever. - * Arduino #2 will wait for at least one second of no signal - * (to synchronize with #1). It will then wait for the same test - * signals. It will log all the status to the serial port. It will - * also indicate status through the LED, which will flash each time a test - * is completed. If there is an error, it will light up for 5 seconds. - * - * The test passes if the LED flashes 19 times, pauses, and then repeats. - * The test fails if the LED lights for 5 seconds. - * - * The test software automatically decides which board is the sender and which is - * the receiver by looking for an input on the send pin, which will indicate - * the sender. You should hook the serial port to the receiver for debugging. - * - * Initially coded 2010 Ken Shirriff http://www.righto.com - */ - -#include - -#if defined(ESP32) -int IR_RECEIVE_PIN = 15; -#else -int IR_RECEIVE_PIN = 11; -#endif -int LED_PIN = 3; - -IRrecv IrReceiver(IR_RECEIVE_PIN); -IRsend IrSender; - -#define RECEIVER 1 -#define SENDER 2 -#define ERROR 3 - -int mode; - -void setup() { - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - - // Check IR_RECEIVE_PIN to decide if we're RECEIVER or SENDER - if (digitalRead(IR_RECEIVE_PIN) == HIGH) { - mode = RECEIVER; - IrReceiver.enableIRIn(); - pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, LOW); - Serial.println("Receiver mode"); - Serial.print(F("Ready to receive IR signals at pin ")); - Serial.println(IR_RECEIVE_PIN); - } else { - mode = SENDER; - Serial.println("Sender mode"); - } -} - -// Wait for the gap between tests, to synchronize with -// the sender. -// Specifically, wait for a signal followed by a gap of at last gap ms. -void waitForGap(unsigned int gap) { - Serial.println("Waiting for gap"); - while (1) { - while (digitalRead(IR_RECEIVE_PIN) == LOW) { - } - unsigned long time = millis(); - while (digitalRead(IR_RECEIVE_PIN) == HIGH) { - if (millis() - time > gap) { - return; - } - } - } -} - -// Dumps out the decode_results structure. -// Call this after IRrecv::decode() -void dump() { - int count = IrReceiver.results.rawlen; - if (IrReceiver.results.decode_type == UNKNOWN) { - Serial.println("Could not decode message"); - } else { - IrReceiver.printResultShort(&Serial); - - Serial.print(" ("); - Serial.print(IrReceiver.results.bits, DEC); - Serial.println(" bits)"); - } - Serial.print("Raw ("); - Serial.print(count, DEC); - Serial.print("): "); - - for (int i = 0; i < count; i++) { - if ((i % 2) == 1) { - Serial.print(IrReceiver.results.rawbuf[i] * MICROS_PER_TICK, DEC); - } else { - Serial.print(-(int) IrReceiver.results.rawbuf[i] * MICROS_PER_TICK, DEC); - } - Serial.print(" "); - } - Serial.println(""); -} - -// Test send or receive. -// If mode is SENDER, send a code of the specified type, value, and bits -// If mode is RECEIVER, receive a code and verify that it is of the -// specified type, value, and bits. For success, the LED is flashed; -// for failure, the mode is set to ERROR. -// The motivation behind this method is that the sender and the receiver -// can do the same test calls, and the mode variable indicates whether -// to send or receive. -void test(const char *label, int type, uint32_t value, int bits) { - if (mode == SENDER) { - Serial.println(label); - if (type == NEC) { - IrSender.sendNEC(value, bits); - } else if (type == SONY) { - IrSender.sendSony(value, bits); - } else if (type == RC5) { - IrSender.sendRC5(value, bits); - } else if (type == RC6) { - IrSender.sendRC6(value, bits); - } else { - Serial.print(label); - Serial.println("Bad type!"); - } - delay(200); - } else if (mode == RECEIVER) { - IrReceiver.resume(); // Receive the next value - unsigned long max_time = millis() + 30000; - Serial.print(label); - - // Wait for decode or timeout - while (!IrReceiver.decode()) { - if (millis() > max_time) { - Serial.println("Timeout receiving data"); - mode = ERROR; - return; - } - } - if (type == IrReceiver.results.decode_type && value == IrReceiver.results.value && bits == IrReceiver.results.bits) { - Serial.println(": OK"); - digitalWrite(LED_PIN, HIGH); - delay(20); - digitalWrite(LED_PIN, LOW); - } else { - Serial.println(": BAD"); - dump(); - mode = ERROR; - } - } -} - -// Test raw send or receive. This is similar to the test method, -// except it send/receives raw data. -void testRaw(const char *label, unsigned int *rawbuf, uint8_t rawlen) { - if (mode == SENDER) { - Serial.println(label); - IrSender.sendRaw(rawbuf, rawlen, 38 /* kHz */); - delay(200); - } else if (mode == RECEIVER) { - IrReceiver.resume(); // Receive the next value - unsigned long max_time = millis() + 30000; - Serial.print(label); - - // Wait for decode or timeout - while (!IrReceiver.decode()) { - if (millis() > max_time) { - Serial.println("Timeout receiving data"); - mode = ERROR; - return; - } - } - - // Received length has extra first element for gap - if (rawlen != IrReceiver.results.rawlen - 1) { - Serial.print("Bad raw length "); - Serial.println(IrReceiver.results.rawlen, DEC); - mode = ERROR; - return; - } - for (unsigned int i = 0; i < rawlen; i++) { - long got = IrReceiver.results.rawbuf[i + 1] * MICROS_PER_TICK; - // Adjust for extra duration of marks - if (i % 2 == 0) { - got -= MARK_EXCESS_MICROS; - } else { - got += MARK_EXCESS_MICROS; - } - // See if close enough, within 25% - if (rawbuf[i] * 1.25 < got || got * 1.25 < rawbuf[i]) { - Serial.println(": BAD"); - dump (); - mode = ERROR; - return; - } - - } - Serial.println(": OK"); - digitalWrite(LED_PIN, HIGH); - delay(20); - digitalWrite(LED_PIN, LOW); - } -} - -// This is the raw data corresponding to NEC 0x12345678 -unsigned int sendbuf[] = { /* NEC format */ -9000, 4500, 560, 560, 560, 560, 560, 560, 560, 1690, /* 1 */ -560, 560, 560, 560, 560, 1690, 560, 560, /* 2 */ -560, 560, 560, 560, 560, 1690, 560, 1690, /* 3 */ -560, 560, 560, 1690, 560, 560, 560, 560, /* 4 */ -560, 560, 560, 1690, 560, 560, 560, 1690, /* 5 */ -560, 560, 560, 1690, 560, 1690, 560, 560, /* 6 */ -560, 560, 560, 1690, 560, 1690, 560, 1690, /* 7 */ -560, 1690, 560, 560, 560, 560, 560, 560, /* 8 */ -560 }; - -void loop() { - if (mode == SENDER) { - delay(2000); // Delay for more than gap to give receiver a better chance to sync. - } else if (mode == RECEIVER) { - waitForGap(1000); - } else if (mode == ERROR) { - // Light up for 5 seconds for error - digitalWrite(LED_PIN, HIGH); - delay(5000); - digitalWrite(LED_PIN, LOW); - mode = RECEIVER; // Try again - return; - } - - // The test suite. - test("SONY1", SONY, 0x123, 12); - test("SONY2", SONY, 0x000, 12); - test("SONY3", SONY, 0xfff, 12); - test("SONY4", SONY, 0x12345, 20); - test("SONY5", SONY, 0x00000, 20); - test("SONY6", SONY, 0xfffff, 20); - test("NEC1", NEC, 0x12345678, 32); - test("NEC2", NEC, 0x00000000, 32); - test("NEC3", NEC, 0xffffffff, 32); - test("NEC4", NEC, REPEAT, 32); - test("RC51", RC5, 0x12345678, 32); - test("RC52", RC5, 0x0, 32); - test("RC53", RC5, 0xffffffff, 32); - test("RC61", RC6, 0x12345678, 32); - test("RC62", RC6, 0x0, 32); - test("RC63", RC6, 0xffffffff, 32); - - // Tests of raw sending and receiving. - // First test sending raw and receiving raw. - // Then test sending raw and receiving decoded NEC - // Then test sending NEC and receiving raw - testRaw("RAW1", sendbuf, 67); - if (mode == SENDER) { - testRaw("RAW2", sendbuf, 67); - test("RAW3", NEC, 0x12345678, 32); - } else { - test("RAW2", NEC, 0x12345678, 32); - testRaw("RAW3", sendbuf, 67); - } -} diff --git a/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino b/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino deleted file mode 100644 index 5500e8114..000000000 --- a/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino +++ /dev/null @@ -1,30 +0,0 @@ -/* - * IRremote: IRsendDemo - demonstrates sending IR codes with IRsend - * An IR LED must be connected to Arduino PWM pin 3. - * Version 0.1 July, 2009 - * Copyright 2009 Ken Shirriff - * http://arcfn.com - * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) - */ -#include - -#define PanasonicAddress 0x4004 // Panasonic address (Pre data) -#define PanasonicPower 0x100BCBD // Panasonic Power button - -#define JVCPower 0xC5E8 - -IRsend irsend; - -void setup() -{ -} - -void loop() { - irsend.sendPanasonic(PanasonicAddress,PanasonicPower); // This should turn your TV on and off - - // JVC commands sometimes need to be sent two or three times with 40 to 60 ms pause in between. - irsend.sendJVC(JVCPower, 16,0); // hex value, 16 bits, no repeat - delayMicroseconds(50); // see http://www.sbprojects.net/knowledge/ir/jvc.php for information - irsend.sendJVC(JVCPower, 16,1); // hex value, 16 bits, repeat - delayMicroseconds(50); -} diff --git a/examples/LGACSendDemo/LGACSendDemo.ino b/examples/LGACSendDemo/LGACSendDemo.ino deleted file mode 100644 index 5ba9f867f..000000000 --- a/examples/LGACSendDemo/LGACSendDemo.ino +++ /dev/null @@ -1,263 +0,0 @@ -#include -#include - - -IRsend irsend; -// not used -int RECV_PIN = 11; -IRrecv irrecv (RECV_PIN); - -const int AC_TYPE = 0; -// 0 : TOWER -// 1 : WALL -// - -int AC_HEAT = 0; -// 0 : cooling -// 1 : heating - -int AC_POWER_ON = 0; -// 0 : off -// 1 : on - -int AC_AIR_ACLEAN = 0; -// 0 : off -// 1 : on --> power on - -int AC_TEMPERATURE = 27; -// temperature : 18 ~ 30 - -int AC_FLOW = 1; -// 0 : low -// 1 : mid -// 2 : high -// if AC_TYPE =1, 3 : change -// - - -const int AC_FLOW_TOWER[3] = {0, 4, 6}; -const int AC_FLOW_WALL[4] = {0, 2, 4, 5}; - -unsigned long AC_CODE_TO_SEND; - -int r = LOW; -int o_r = LOW; - -byte a, b; - -void ac_send_code(unsigned long code) -{ - Serial.print("code to send : "); - Serial.print(code, BIN); - Serial.print(" : "); - Serial.println(code, HEX); - - irsend.sendLG(code, 28); -} - -void ac_activate(int temperature, int air_flow) -{ - - int AC_MSBITS1 = 8; - int AC_MSBITS2 = 8; - int AC_MSBITS3 = 0; - int AC_MSBITS4 ; - if ( AC_HEAT == 1 ) { - // heating - AC_MSBITS4 = 4; - } else { - // cooling - AC_MSBITS4 = 0; - } - int AC_MSBITS5 = temperature - 15; - int AC_MSBITS6 ; - - if ( AC_TYPE == 0) { - AC_MSBITS6 = AC_FLOW_TOWER[air_flow]; - } else { - AC_MSBITS6 = AC_FLOW_WALL[air_flow]; - } - - int AC_MSBITS7 = (AC_MSBITS3 + AC_MSBITS4 + AC_MSBITS5 + AC_MSBITS6) & B00001111; - - AC_CODE_TO_SEND = AC_MSBITS1 << 4 ; - AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS2) << 4; - AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS3) << 4; - AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS4) << 4; - AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS5) << 4; - AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS6) << 4; - AC_CODE_TO_SEND = (AC_CODE_TO_SEND + AC_MSBITS7); - - ac_send_code(AC_CODE_TO_SEND); - - AC_POWER_ON = 1; - AC_TEMPERATURE = temperature; - AC_FLOW = air_flow; -} - -void ac_change_air_swing(int air_swing) -{ - if ( AC_TYPE == 0) { - if ( air_swing == 1) { - AC_CODE_TO_SEND = 0x881316B; - } else { - AC_CODE_TO_SEND = 0x881317C; - } - } else { - if ( air_swing == 1) { - AC_CODE_TO_SEND = 0x8813149; - } else { - AC_CODE_TO_SEND = 0x881315A; - } - } - - ac_send_code(AC_CODE_TO_SEND); -} - -void ac_power_down() -{ - AC_CODE_TO_SEND = 0x88C0051; - - ac_send_code(AC_CODE_TO_SEND); - - AC_POWER_ON = 0; -} - -void ac_air_clean(int air_clean) -{ - if ( air_clean == 1) { - AC_CODE_TO_SEND = 0x88C000C; - } else { - AC_CODE_TO_SEND = 0x88C0084; - } - - ac_send_code(AC_CODE_TO_SEND); - - AC_AIR_ACLEAN = air_clean; -} - -void setup() -{ - Serial.begin(115200); - delay(1000); - Wire.begin(7); - Wire.onReceive(receiveEvent); - - Serial.println(" - - - T E S T - - - "); - - /* test - ac_activate(25, 1); - delay(5000); - ac_activate(27, 2); - delay(5000); - - */ -} - -void loop() -{ - - - ac_activate(25, 1); - delay(5000); - ac_activate(27, 0); - delay(5000); - - - if ( r != o_r) { - - /* - # a : mode or temp b : air_flow, temp, swing, clean, cooling/heating - # 18 ~ 30 : temp 0 ~ 2 : flow // on - # 0 : off 0 - # 1 : on 0 - # 2 : air_swing 0 or 1 - # 3 : air_clean 0 or 1 - # 4 : air_flow 0 ~ 2 : flow - # 5 : temp 18 ~ 30 - # + : temp + 1 - # - : temp - 1 - # m : change cooling to air clean, air clean to cooling - */ - Serial.print("a : "); - Serial.print(a); - Serial.print(" b : "); - Serial.println(b); - - switch (a) { - case 0: // off - ac_power_down(); - break; - case 1: // on - ac_activate(AC_TEMPERATURE, AC_FLOW); - break; - case 2: - if ( b == 0 || b == 1 ) { - ac_change_air_swing(b); - } - break; - case 3: // 1 : clean on, power on - if ( b == 0 || b == 1 ) { - ac_air_clean(b); - } - break; - case 4: - if ( 0 <= b && b <= 2 ) { - ac_activate(AC_TEMPERATURE, b); - } - break; - case 5: - if (18 <= b && b <= 30 ) { - ac_activate(b, AC_FLOW); - } - break; - case '+': - if ( 18 <= AC_TEMPERATURE && AC_TEMPERATURE <= 29 ) { - ac_activate((AC_TEMPERATURE + 1), AC_FLOW); - } - break; - case '-': - if ( 19 <= AC_TEMPERATURE && AC_TEMPERATURE <= 30 ) { - ac_activate((AC_TEMPERATURE - 1), AC_FLOW); - } - break; - case 'm': - /* - if ac is on, 1) turn off, 2) turn on ac_air_clean(1) - if ac is off, 1) turn on, 2) turn off ac_air_clean(0) - */ - if ( AC_POWER_ON == 1 ) { - ac_power_down(); - delay(100); - ac_air_clean(1); - } else { - if ( AC_AIR_ACLEAN == 1) { - ac_air_clean(0); - delay(100); - } - ac_activate(AC_TEMPERATURE, AC_FLOW); - } - break; - default: - if ( 18 <= a && a <= 30 ) { - if ( 0 <= b && b <= 2 ) { - ac_activate(a, b); - } - } - } - - o_r = r ; - } - delay(100); -} - - - -void receiveEvent(int howMany) -{ - a = Wire.read(); - b = Wire.read(); - r = !r ; -} - - diff --git a/examples/LegoPowerFunctionsSendDemo/LegoPowerFunctionsSendDemo.ino b/examples/LegoPowerFunctionsSendDemo/LegoPowerFunctionsSendDemo.ino deleted file mode 100644 index 1afafe5b6..000000000 --- a/examples/LegoPowerFunctionsSendDemo/LegoPowerFunctionsSendDemo.ino +++ /dev/null @@ -1,21 +0,0 @@ -/* - * LegoPowerFunctionsSendDemo: LEGO Power Functions - * Copyright (c) 2016 Philipp Henkel - */ - -#include - -IRsend irsend; - -void setup() { -} - -void loop() { - // Send repeated command "channel 1, blue forward, red backward" - irsend.sendLegoPowerFunctions(0x197); - delay(2000); - - // Send single command "channel 1, blue forward, red backward" - irsend.sendLegoPowerFunctions(0x197, false); - delay(2000); -} diff --git a/examples/LegoPowerFunctionsTests/LegoPowerFunctionsTests.ino b/examples/LegoPowerFunctionsTests/LegoPowerFunctionsTests.ino deleted file mode 100644 index d43779e59..000000000 --- a/examples/LegoPowerFunctionsTests/LegoPowerFunctionsTests.ino +++ /dev/null @@ -1,206 +0,0 @@ -/* - * LegoPowerFunctionsTest: LEGO Power Functions Tests - * Copyright (c) 2016, 2017 Philipp Henkel - */ - -#include -#include "ir_Lego_PF_BitStreamEncoder.h" - -void runBitStreamEncoderTests(); - -void setup() { - pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(115200); -#if defined(__AVR_ATmega32U4__) - while (!Serial) - ; //delay for Leonardo, but this loops forever for Maple Serial -#endif -#if defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) - delay(2000); // To be able to connect Serial monitor after reset and before first printout -#endif - // Just to know which program is running on my Arduino - Serial.println(F("START " __FILE__ " from " __DATE__)); - delay(1000); // wait for reset triggered by serial connection - runBitStreamEncoderTests(); -} - -void loop() { -} - -void runBitStreamEncoderTests() { - Serial.println(); - Serial.println("BitStreamEncoder Tests"); - static LegoPfBitStreamEncoder bitStreamEncoder; - testStartBit(bitStreamEncoder); - testLowBit(bitStreamEncoder); - testHighBit(bitStreamEncoder); - testMessageBitCount(bitStreamEncoder); - testMessageBitCountRepeat(bitStreamEncoder); - testMessage407(bitStreamEncoder); - testMessage407Repeated(bitStreamEncoder); - testGetChannelId1(bitStreamEncoder); - testGetChannelId2(bitStreamEncoder); - testGetChannelId3(bitStreamEncoder); - testGetChannelId4(bitStreamEncoder); - testGetMessageLengthAllHigh(bitStreamEncoder); - testGetMessageLengthAllLow(bitStreamEncoder); -} - -void logTestResult(bool testPassed) { - if (testPassed) { - Serial.println("OK"); - } else { - Serial.println("FAIL ############"); - } -} - -void testStartBit(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testStartBit "); - bitStreamEncoder.reset(0, false); - int startMark = bitStreamEncoder.getMarkDuration(); - int startPause = bitStreamEncoder.getPauseDuration(); - logTestResult(startMark == 158 && startPause == 1184 - 158); -} - -void testLowBit(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testLowBit "); - bitStreamEncoder.reset(0, false); - bitStreamEncoder.next(); - int lowMark = bitStreamEncoder.getMarkDuration(); - int lowPause = bitStreamEncoder.getPauseDuration(); - logTestResult(lowMark == 158 && lowPause == 421 - 158); -} - -void testHighBit(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testHighBit "); - bitStreamEncoder.reset(0xFFFF, false); - bitStreamEncoder.next(); - int highMark = bitStreamEncoder.getMarkDuration(); - int highPause = bitStreamEncoder.getPauseDuration(); - logTestResult(highMark == 158 && highPause == 711 - 158); -} - -void testMessageBitCount(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testMessageBitCount "); - bitStreamEncoder.reset(0xFFFF, false); - int bitCount = 1; - while (bitStreamEncoder.next()) { - bitCount++; - } - logTestResult(bitCount == 18); -} - -boolean check(LegoPfBitStreamEncoder& bitStreamEncoder, unsigned long markDuration, unsigned long pauseDuration) { - bool result = true; - result = result && bitStreamEncoder.getMarkDuration() == markDuration; - result = result && bitStreamEncoder.getPauseDuration() == pauseDuration; - return result; -} - -boolean checkNext(LegoPfBitStreamEncoder& bitStreamEncoder, unsigned long markDuration, unsigned long pauseDuration) { - bool result = bitStreamEncoder.next(); - result = result && check(bitStreamEncoder, markDuration, pauseDuration); - return result; -} - -boolean checkDataBitsOfMessage407(LegoPfBitStreamEncoder& bitStreamEncoder) { - bool result = true; - result = result && checkNext(bitStreamEncoder, 158, 263); - result = result && checkNext(bitStreamEncoder, 158, 263); - result = result && checkNext(bitStreamEncoder, 158, 263); - result = result && checkNext(bitStreamEncoder, 158, 263); - result = result && checkNext(bitStreamEncoder, 158, 263); - result = result && checkNext(bitStreamEncoder, 158, 263); - result = result && checkNext(bitStreamEncoder, 158, 263); - result = result && checkNext(bitStreamEncoder, 158, 553); - result = result && checkNext(bitStreamEncoder, 158, 553); - result = result && checkNext(bitStreamEncoder, 158, 263); - result = result && checkNext(bitStreamEncoder, 158, 263); - result = result && checkNext(bitStreamEncoder, 158, 553); - result = result && checkNext(bitStreamEncoder, 158, 263); - result = result && checkNext(bitStreamEncoder, 158, 553); - result = result && checkNext(bitStreamEncoder, 158, 553); - result = result && checkNext(bitStreamEncoder, 158, 553); - return result; -} - -void testMessage407(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testMessage407 "); - bitStreamEncoder.reset(407, false); - bool result = true; - result = result && check(bitStreamEncoder, 158, 1026); - result = result && checkDataBitsOfMessage407(bitStreamEncoder); - result = result && checkNext(bitStreamEncoder, 158, 1026); - result = result && !bitStreamEncoder.next(); - logTestResult(result); -} - -void testMessage407Repeated(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testMessage407Repeated "); - bitStreamEncoder.reset(407, true); - bool result = true; - result = result && check(bitStreamEncoder, 158, 1026); - result = result && checkDataBitsOfMessage407(bitStreamEncoder); - result = result && checkNext(bitStreamEncoder, 158, 1026L + 5L * 16000L - 10844L); - result = result && checkNext(bitStreamEncoder, 158, 1026); - result = result && checkDataBitsOfMessage407(bitStreamEncoder); - result = result && checkNext(bitStreamEncoder, 158, 1026L + 5L * 16000L - 10844L); - result = result && checkNext(bitStreamEncoder, 158, 1026); - result = result && checkDataBitsOfMessage407(bitStreamEncoder); - result = result && checkNext(bitStreamEncoder, 158, 1026L + 8L * 16000L - 10844L); - result = result && checkNext(bitStreamEncoder, 158, 1026); - result = result && checkDataBitsOfMessage407(bitStreamEncoder); - result = result && checkNext(bitStreamEncoder, 158, 1026L + 8L * 16000L - 10844L); - result = result && checkNext(bitStreamEncoder, 158, 1026); - result = result && checkDataBitsOfMessage407(bitStreamEncoder); - result = result && checkNext(bitStreamEncoder, 158, 1026); - result = result && !bitStreamEncoder.next(); - logTestResult(result); -} - -void testMessageBitCountRepeat(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testMessageBitCountRepeat "); - bitStreamEncoder.reset(0xFFFF, true); - int bitCount = 1; - while (bitStreamEncoder.next()) { - bitCount++; - } - logTestResult(bitCount == 5 * 18); -} - -void testGetChannelId1(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testGetChannelId1 "); - bitStreamEncoder.reset(407, false); - logTestResult(bitStreamEncoder.getChannelId() == 1); -} - -void testGetChannelId2(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testGetChannelId2 "); - bitStreamEncoder.reset(4502, false); - logTestResult(bitStreamEncoder.getChannelId() == 2); -} - -void testGetChannelId3(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testGetChannelId3 "); - bitStreamEncoder.reset(8597, false); - logTestResult(bitStreamEncoder.getChannelId() == 3); -} - -void testGetChannelId4(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testGetChannelId4 "); - bitStreamEncoder.reset(12692, false); - logTestResult(bitStreamEncoder.getChannelId() == 4); -} - -void testGetMessageLengthAllHigh(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testGetMessageLengthAllHigh "); - bitStreamEncoder.reset(0xFFFF, false); - logTestResult(bitStreamEncoder.getMessageLength() == 13744); -} - -void testGetMessageLengthAllLow(LegoPfBitStreamEncoder& bitStreamEncoder) { - Serial.print(" testGetMessageLengthAllLow "); - bitStreamEncoder.reset(0x0, false); - logTestResult(bitStreamEncoder.getMessageLength() == 9104); -} diff --git a/examples/MicroGirs/MicroGirs.ino b/examples/MicroGirs/MicroGirs.ino index df36013b5..ca2a9b18a 100644 --- a/examples/MicroGirs/MicroGirs.ino +++ b/examples/MicroGirs/MicroGirs.ino @@ -61,27 +61,28 @@ * In IrSqrutinizer, recognition of repeating signals will therefore not work. * The size of the data is platform dependent ("unsigned int", which is 16 bit on AVR boards, 32 bits on 32 bit boards). * - * For minimal footprint, undefine all DECODE* and SEND_* in IRremote.h. - * - * For optimal results, try for example to set _GAP to 100000 - * (what a brilliant variable name!!) and RAW_BUFFER_LENGTH to 251 - * in IRremoteInt.h. */ +#include -// Change the following two entries if desired +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. -/** - * Input Pin used by the receiver, can be arbitrary (almost...) - */ -#define INPUTPIN 5 +#if !defined(RAW_BUFFER_LENGTH) +// For air condition remotes it may require up to 750. Default is 200. +# if (defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF) +#define RAW_BUFFER_LENGTH 360 +# else +#define RAW_BUFFER_LENGTH 750 +# endif +#endif /** * Baud rate for the serial/USB connection. * (115200 is the default for IrScrutinizer and Lirc.) */ #define BAUDRATE 115200 +#define NO_DECODER -#include +#include "IRremote.hpp" #include /** @@ -107,14 +108,14 @@ /** * The modules supported, as given by the "modules" command. */ -#ifdef TRANSMIT -#ifdef RECEIVE +#if defined(TRANSMIT) +#if defined(RECEIVE) #define modulesSupported "base transmit receive" #else // ! RECEIVE #define modulesSupported "base transmit" #endif #else // !TRANSMIT -#ifdef RECEIVE +#if defined(RECEIVE) #define modulesSupported "base receive" #else // ! RECETVE #error At lease one of TRANSMIT and RECEIVE must be defined @@ -146,26 +147,12 @@ typedef unsigned frequency_t; // max 65535, unless 32-bit /** * Type used for durations in micro seconds. */ -typedef unsigned microseconds_t; // max 65535, unless 32-bit +typedef uint16_t microseconds_t; // max 65535 static const microseconds_t DUMMYENDING = 40000U; static const frequency_t FREQUENCY_T_MAX = UINT16_MAX; static const frequency_t MICROSECONDS_T_MAX = UINT16_MAX; -#ifdef RECEIVE -/** - * Instance of the IRremote class. - */ -IRrecv irRecv(INPUTPIN); -#endif - -#ifdef TRANSMIT -/*} - * Instance of the IRremote class. - */ -IRsend irSend; -#endif - /** * Our own tokenizer class. Breaks the command line into tokens. * Usage outside of this package is discouraged. @@ -175,12 +162,11 @@ private: static const int invalidIndex = -1; int index; // signed since invalidIndex is possible - const String& payload; + const String &payload; void trim(); public: Tokenizer(const String &str); - virtual ~Tokenizer(); String getToken(); String getRest(); @@ -192,10 +178,8 @@ public: static const int invalid = INT_MAX; }; -Tokenizer::Tokenizer(const String& str) : index(0), payload(str) { -} - -Tokenizer::~Tokenizer() { +Tokenizer::Tokenizer(const String &str) : + index(0), payload(str) { } String Tokenizer::getRest() { @@ -205,26 +189,22 @@ String Tokenizer::getRest() { } String Tokenizer::getLine() { - if (index == invalidIndex) - return String(""); + if (index == invalidIndex) return String(""); int i = payload.indexOf('\n', index); String s = (i > 0) ? payload.substring(index, i) : payload.substring(index); - index = (i > 0) ? i+1 : invalidIndex; + index = (i > 0) ? i + 1 : invalidIndex; return s; } String Tokenizer::getToken() { - if (index < 0) - return String(""); + if (index < 0) return String(""); int i = payload.indexOf(' ', index); String s = (i > 0) ? payload.substring(index, i) : payload.substring(index); index = (i > 0) ? i : invalidIndex; - if (index != invalidIndex) - if (index != invalidIndex) - while (payload.charAt(index) == ' ') - index++; + if (index != invalidIndex) if (index != invalidIndex) while (payload.charAt(index) == ' ') + index++; return s; } @@ -244,9 +224,9 @@ frequency_t Tokenizer::getFrequency() { } ///////////////// end Tokenizer ///////////////////////////////// -#ifdef TRANSMIT +#if defined(TRANSMIT) static inline unsigned hz2khz(frequency_t hz) { - return (hz + 500)/1000; + return (hz + 500) / 1000; } /** @@ -266,49 +246,45 @@ static inline unsigned hz2khz(frequency_t hz) { * @param frequency Modulation frequency, in Hz (not kHz as normally in IRremote) * @param times Number of times to send the signal, in the sense above. */ -static void sendRaw(const microseconds_t intro[], unsigned lengthIntro, - const microseconds_t repeat[], unsigned lengthRepeat, - const microseconds_t ending[], unsigned lengthEnding, - frequency_t frequency, unsigned times) { - if (lengthIntro > 0U) - irSend.sendRaw(intro, lengthIntro, hz2khz(frequency)); - if (lengthRepeat > 0U) - for (unsigned i = 0U; i < times - (lengthIntro > 0U); i++) - irSend.sendRaw(repeat, lengthRepeat, hz2khz(frequency)); - if (lengthEnding > 0U) - irSend.sendRaw(ending, lengthEnding, hz2khz(frequency)); +static void sendRaw(const microseconds_t intro[], unsigned lengthIntro, const microseconds_t repeat[], unsigned lengthRepeat, + const microseconds_t ending[], unsigned lengthEnding, frequency_t frequency, unsigned times) { + if (lengthIntro > 0U) IrSender.sendRaw(intro, lengthIntro, hz2khz(frequency)); + if (lengthRepeat > 0U) for (unsigned i = 0U; i < times - (lengthIntro > 0U); i++) + IrSender.sendRaw(repeat, lengthRepeat, hz2khz(frequency)); + if (lengthEnding > 0U) IrSender.sendRaw(ending, lengthEnding, hz2khz(frequency)); } #endif // TRANSMIT -#ifdef RECEIVE -/** - * Reads a command from the stream given as argument. - * @param stream Stream to read from, typically Serial. - */ -static void receive(Stream& stream) { - irRecv.enableIRIn(); - irRecv.resume(); // Receive the next value - - while (!irRecv.decode()) { - } - irRecv.disableIRIn(); +#if defined(RECEIVE) - dump(stream); -} - -static void dump(Stream& stream) { - unsigned int count = irRecv.results.rawlen; +static void dump(Stream &stream) { + unsigned int count = IrReceiver.decodedIRData.rawDataPtr->rawlen; // If buffer gets full, count = RAW_BUFFER_LENGTH, which is odd, // and IrScrutinizer does not like that. - count &= ~1; + count &= ~1; for (unsigned int i = 1; i < count; i++) { stream.write(i & 1 ? '+' : '-'); - stream.print(irRecv.results.rawbuf[i] * MICROS_PER_TICK, DEC); + stream.print(IrReceiver.decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK, DEC); stream.print(" "); } stream.print('-'); stream.println(DUMMYENDING); } + +/** + * Reads a command from the stream given as argument. + * @param stream Stream to read from, typically Serial. + */ +static void receive(Stream &stream) { + IrReceiver.start(); + + while (!IrReceiver.decode()) { + } + IrReceiver.stop(); + + dump(stream); +} + #endif // RECEIVE /** @@ -316,23 +292,29 @@ static void dump(Stream& stream) { */ void setup() { Serial.begin(BAUDRATE); - while (!Serial) - ; // wait for serial port to connect. Serial.println(F(PROGNAME " " VERSION)); - //Serial.setTimeout(SERIALTIMEOUT); -/* -#ifdef RECEIVE - // There is unfortunately no disableIRIn in IRremote. - // Therefore, turn it on, and leave it on. - // We _hope_ that it will not interfere with sending. - irRecv.enableIRIn(); + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + +#if defined(RECEIVE) + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); + Serial.print(F("at pin ")); #endif -*/ -} +#if defined(IR_SEND_PIN) + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin +#else + IrSender.begin(3, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN); // Specify send pin and enable feedback LED at default feedback LED pin +#endif + +} -static String readCommand(Stream& stream) { +static String readCommand(Stream &stream) { while (stream.available() == 0) { } @@ -341,7 +323,7 @@ static String readCommand(Stream& stream) { return line; } -static void processCommand(const String& line, Stream& stream) { +static void processCommand(const String &line, Stream &stream) { Tokenizer tokenizer(line); String cmd = tokenizer.getToken(); @@ -353,47 +335,47 @@ static void processCommand(const String& line, Stream& stream) { } switch (cmd[0]) { - case 'm': - stream.println(F(modulesSupported)); - break; + case 'm': + stream.println(F(modulesSupported)); + break; -#ifdef RECEIVE - case 'r': // receive +#if defined(RECEIVE) + case 'r': // receive //case 'a': //case 'c': - receive(stream); - break; + receive(stream); + break; #endif // RECEIVE -#ifdef TRANSMIT - case 's': // send - { - // TODO: handle unparsable data gracefully - unsigned noSends = (unsigned) tokenizer.getInt(); - frequency_t frequency = tokenizer.getFrequency(); - unsigned introLength = (unsigned) tokenizer.getInt(); - unsigned repeatLength = (unsigned) tokenizer.getInt(); - unsigned endingLength = (unsigned) tokenizer.getInt(); - microseconds_t intro[introLength]; - microseconds_t repeat[repeatLength]; - microseconds_t ending[endingLength]; - for (unsigned i = 0; i < introLength; i++) - intro[i] = tokenizer.getMicroseconds(); - for (unsigned i = 0; i < repeatLength; i++) - repeat[i] = tokenizer.getMicroseconds(); - for (unsigned i = 0; i < endingLength; i++) - ending[i] = tokenizer.getMicroseconds(); - sendRaw(intro, introLength, repeat, repeatLength, ending, endingLength, frequency, noSends); - stream.println(F(okString)); - } - break; +#if defined(TRANSMIT) + case 's': // send + { + // TODO: handle unparsable data gracefully + unsigned noSends = (unsigned) tokenizer.getInt(); + frequency_t frequency = tokenizer.getFrequency(); + unsigned introLength = (unsigned) tokenizer.getInt(); + unsigned repeatLength = (unsigned) tokenizer.getInt(); + unsigned endingLength = (unsigned) tokenizer.getInt(); + microseconds_t intro[introLength]; + microseconds_t repeat[repeatLength]; + microseconds_t ending[endingLength]; + for (unsigned i = 0; i < introLength; i++) + intro[i] = tokenizer.getMicroseconds(); + for (unsigned i = 0; i < repeatLength; i++) + repeat[i] = tokenizer.getMicroseconds(); + for (unsigned i = 0; i < endingLength; i++) + ending[i] = tokenizer.getMicroseconds(); + sendRaw(intro, introLength, repeat, repeatLength, ending, endingLength, frequency, noSends); + stream.println(F(okString)); + } + break; #endif // TRANSMIT - case 'v': // version - stream.println(F(PROGNAME " " VERSION)); - break; - default: - stream.println(F(errorString)); + case 'v': // version + stream.println(F(PROGNAME " " VERSION)); + break; + default: + stream.println(F(errorString)); } } diff --git a/examples/MicroGirs/PinDefinitionsAndMore.h b/examples/MicroGirs/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/MicroGirs/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/MultipleSendPins/MultipleSendPins.ino b/examples/MultipleSendPins/MultipleSendPins.ino new file mode 100644 index 000000000..33566bcbc --- /dev/null +++ b/examples/MultipleSendPins/MultipleSendPins.ino @@ -0,0 +1,94 @@ +/* + * MultipleSendPins.cpp + * + * Demonstrates sending IR codes toggling between 2 different send pins. + * Based on SimpleSender. + * + * Copyright (C) 2025 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * MIT License + */ +#include + +#if !defined(ARDUINO_ESP32C3_DEV) // This is due to a bug in RISC-V compiler, which requires unused function sections :-(. +#define DISABLE_CODE_FOR_RECEIVER // Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. +#endif +//#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition + +#include // include the library + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + Serial.print(F("Send IR signals alternating at pin 3 and 4")); + + /* + * The IR library setup. That's all! + */ + IrSender.begin(3); // Start with pin3 as send pin and enable feedback LED at default feedback LED pin + disableLEDFeedback(); // Disable feedback LED at default feedback LED pin +} + +/* + * Set up the data to be sent. + * For most protocols, the data is build up with a constant 8 (or 16 byte) address + * and a variable 8 bit command. + * There are exceptions like Sony and Denon, which have 5 bit address. + */ +uint8_t sCommand = 0x34; +uint8_t sRepeats = 0; + +void loop() { + /* + * Print current send values + */ + Serial.println(); + Serial.print(F("Send now: address=0x00, command=0x")); + Serial.print(sCommand, HEX); + Serial.print(F(", repeats=")); + Serial.print(sRepeats); + Serial.println(); + + Serial.println(F("Send standard NEC with 8 bit address")); + Serial.flush(); + + // Receiver output for the first loop must be: Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 (32 bits) + IrSender.sendNEC(0x00, sCommand, sRepeats); + + /* + * If you want to send a raw HEX value directly like e.g. 0xCB340102 you must use sendNECRaw() + */ +// Serial.println(F("Send 32 bit LSB 0xCB340102 with NECRaw()")); +// IrSender.sendNECRaw(0xCB340102, sRepeats); + /* + * If you want to send an "old" MSB HEX value used by IRremote versions before 3.0 like e.g. 0x40802CD3 you must use sendNECMSB() + */ +// Serial.println(F("Send old 32 bit MSB 0x40802CD3 with sendNECMSB()")); +// IrSender.sendNECMSB(0x40802CD3, 32, sRepeats); + /* + * Increment send values + */ + sCommand += 0x11; + sRepeats++; + // clip repeats at 4 + if (sRepeats > 4) { + sRepeats = 4; + } + + /* + * Toggle between send pin 3 and 4 + */ + if (IrSender.sendPin == 3) { + IrSender.setSendPin(4); + } else { + IrSender.setSendPin(3); + } + delay(1000); // delay must be greater than 5 ms (RECORD_GAP_MICROS), otherwise the receiver sees it as one long signal +} diff --git a/examples/ReceiveAndSend/PinDefinitionsAndMore.h b/examples/ReceiveAndSend/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/ReceiveAndSend/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/ReceiveAndSend/ReceiveAndSend.ino b/examples/ReceiveAndSend/ReceiveAndSend.ino new file mode 100644 index 000000000..591579d85 --- /dev/null +++ b/examples/ReceiveAndSend/ReceiveAndSend.ino @@ -0,0 +1,254 @@ +/* + * ReceiveAndSend.cpp + * + * Record and play back last received IR signal at button press. + * The logic is: + * If the button is pressed, send the IR code. + * If an IR code is received, record it. + * If the protocol is unknown or not enabled, store it as raw data for later sending. + * + * An example for simultaneous receiving and sending is in the UnitTest example. + * + * An IR detector/demodulator must be connected to the input IR_RECEIVE_PIN. + * + * A button must be connected between the input SEND_BUTTON_PIN and ground. + * A visible LED can be connected to STATUS_PIN to provide status. + * + * See also https://dronebotworkshop.com/ir-remotes/#ReceiveAndSend_Code + * + * Initially coded 2009 Ken Shirriff http://www.righto.com + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2009-2025 Ken Shirriff, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. + +/* + * Specify which protocol(s) should be used for decoding. + * If no protocol is defined, all protocols (except Bang&Olufsen) are active. + * This must be done before the #include + */ +//#define DECODE_DENON // Includes Sharp +//#define DECODE_JVC +//#define DECODE_KASEIKYO +//#define DECODE_PANASONIC // alias for DECODE_KASEIKYO +//#define DECODE_LG +//#define DECODE_NEC // Includes Apple and Onkyo +//#define DECODE_SAMSUNG +//#define DECODE_SONY +//#define DECODE_RC5 +//#define DECODE_RC6 + +//#define DECODE_BOSEWAVE +//#define DECODE_LEGO_PF +//#define DECODE_MAGIQUEST +//#define DECODE_WHYNTER +//#define DECODE_FAST +// + +#if !defined(RAW_BUFFER_LENGTH) +// For air condition remotes it may require up to 750. Default is 200. +# if !((defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF)) +#define RAW_BUFFER_LENGTH 700 // we require 2 buffer of this size for this example +# endif +#endif + +//#define EXCLUDE_UNIVERSAL_PROTOCOLS // Saves up to 1000 bytes program memory. +#define EXCLUDE_EXOTIC_PROTOCOLS // saves around 650 bytes program memory if all other protocols are active +//#define NO_LED_FEEDBACK_CODE // saves 92 bytes program memory +//#define RECORD_GAP_MICROS 12000 // Default is 8000. Activate it for some LG air conditioner protocols +//#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. +//#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition + +// MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding, +// to compensate for the signal forming of different IR receiver modules. See also IRremote.hpp line 142. +#define MARK_EXCESS_MICROS 20 // Adapt it to your IR receiver module. 20 is recommended for the cheap VS1838 modules. + +//#define DEBUG // Activate this for lots of lovely debug output from the decoders. + +#include + +int SEND_BUTTON_PIN = APPLICATION_PIN; + +int DELAY_BETWEEN_REPEAT = 50; + +// Storage for the recorded code +struct storedIRDataStruct { + IRData receivedIRData; + // extensions for sendRaw + uint8_t rawCode[RAW_BUFFER_LENGTH]; // The durations if raw + uint8_t rawCodeLength; // The length of the code +} sStoredIRData; + +bool sSendButtonWasActive; + +void storeCode(); +void sendCode(storedIRDataStruct *aIRDataToSend); + +void setup() { + pinMode(SEND_BUTTON_PIN, INPUT_PULLUP); + + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); + + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin + Serial.print(F("Ready to send IR signal (with repeats) at pin " STR(IR_SEND_PIN) " as long as button at pin ")); + Serial.print(SEND_BUTTON_PIN); + Serial.println(F(" is pressed.")); +} + +void loop() { + + // If button pressed, send the code. + bool tSendButtonIsActive = (digitalRead(SEND_BUTTON_PIN) == LOW); // Button pin is active LOW + + /* + * Check for current button state + */ + if (tSendButtonIsActive) { + if (!sSendButtonWasActive) { + Serial.println(F("Stop receiving")); + IrReceiver.stop(); + } + /* + * Button pressed -> send stored data + */ + Serial.print(F("Button pressed, now sending ")); + if (sSendButtonWasActive == tSendButtonIsActive) { + Serial.print(F("repeat ")); + sStoredIRData.receivedIRData.flags = IRDATA_FLAGS_IS_REPEAT; + } else { + sStoredIRData.receivedIRData.flags = IRDATA_FLAGS_EMPTY; + } + Serial.flush(); // To avoid disturbing the software PWM generation by serial output interrupts + sendCode(&sStoredIRData); + delay(DELAY_BETWEEN_REPEAT); // Wait a bit between retransmissions + + } else if (sSendButtonWasActive) { + /* + * Button is just released -> activate receiving + */ + // Restart receiver + Serial.println(F("Button released -> start receiving")); + IrReceiver.start(); + delay(100); // Button debouncing + + } else if (IrReceiver.decode()) { + /* + * Button is not pressed and data available -> store received data and resume + */ + storeCode(); + IrReceiver.resume(); // resume receiver + } + + sSendButtonWasActive = tSendButtonIsActive; +} + +// Stores the code for later playback in sStoredIRData +// Most of this code is just logging +void storeCode() { + if (IrReceiver.decodedIRData.rawDataPtr->rawlen < 4) { + Serial.print(F("Ignore data with rawlen=")); + Serial.println(IrReceiver.decodedIRData.rawDataPtr->rawlen); + return; + } + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) { + Serial.println(F("Ignore repeat")); + return; + } + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_AUTO_REPEAT) { + Serial.println(F("Ignore autorepeat")); + return; + } + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_PARITY_FAILED) { + Serial.println(F("Ignore parity error")); + return; + } + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) { + Serial.println(F("Overflow occurred, raw data did not fit into " STR(RAW_BUFFER_LENGTH) " byte raw buffer")); + return; + } + /* + * Copy decoded data + */ + sStoredIRData.receivedIRData = IrReceiver.decodedIRData; + + auto tProtocol = sStoredIRData.receivedIRData.protocol; + if (tProtocol == UNKNOWN || tProtocol == PULSE_WIDTH || tProtocol == PULSE_DISTANCE) { + // TODO: support PULSE_WIDTH and PULSE_DISTANCE with IrSender.write + sStoredIRData.rawCodeLength = IrReceiver.decodedIRData.rawDataPtr->rawlen - 1; + /* + * Store the current raw data in a dedicated array for later usage + */ + IrReceiver.compensateAndStoreIRResultInArray(sStoredIRData.rawCode); + /* + * Print info + */ + Serial.print(F("Received unknown code and store ")); + Serial.print(IrReceiver.decodedIRData.rawDataPtr->rawlen - 1); + Serial.println(F(" timing entries as raw in buffer of size " STR(RAW_BUFFER_LENGTH))); + IrReceiver.printIRResultRawFormatted(&Serial, true); // Output the results in RAW format + + } else { + IrReceiver.printIRResultShort(&Serial); + IrReceiver.printIRSendUsage(&Serial); + sStoredIRData.receivedIRData.flags = 0; // clear flags -esp. repeat- for later sending + Serial.println(); + } +} + +void sendCode(storedIRDataStruct *aIRDataToSend) { + auto tProtocol = aIRDataToSend->receivedIRData.protocol; + if (tProtocol == UNKNOWN || tProtocol == PULSE_WIDTH || tProtocol == PULSE_DISTANCE /* i.e. raw */) { + // Assume 38 KHz + IrSender.sendRaw(aIRDataToSend->rawCode, aIRDataToSend->rawCodeLength, 38); + + Serial.print(F("raw ")); + Serial.print(aIRDataToSend->rawCodeLength); + Serial.println(F(" marks or spaces")); + } else { + /* + * Use the write function, which does the switch for different protocols + */ + IrSender.write(&aIRDataToSend->receivedIRData); + printIRResultShort(&Serial, &aIRDataToSend->receivedIRData); + } +} + diff --git a/examples/ReceiveAndSendDistanceWidth/PinDefinitionsAndMore.h b/examples/ReceiveAndSendDistanceWidth/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/ReceiveAndSendDistanceWidth/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/ReceiveAndSendDistanceWidth/ReceiveAndSendDistanceWidth.ino b/examples/ReceiveAndSendDistanceWidth/ReceiveAndSendDistanceWidth.ino new file mode 100644 index 000000000..a932e91ac --- /dev/null +++ b/examples/ReceiveAndSendDistanceWidth/ReceiveAndSendDistanceWidth.ino @@ -0,0 +1,186 @@ +/* + * ReceiveAndSendDistanceWidth.cpp + * + * Record and play back last received distance width IR signal at button press. + * Using DistanceWidthProtocol covers a lot of known and unknown IR protocols, + * and requires less memory than raw protocol. + * + * The logic is: + * If the button is pressed, send the IR code. + * If an IR code is received, record it. + * + * An example for simultaneous receiving and sending is in the UnitTest example. + * + * An IR detector/demodulator must be connected to the input IR_RECEIVE_PIN. + * + * A button must be connected between the input SEND_BUTTON_PIN and ground. + * A visible LED can be connected to STATUS_PIN to provide status. + * + * See also https://dronebotworkshop.com/ir-remotes/#ReceiveAndSendDistanceWidth_Code + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2023 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. +#if !defined(IR_SEND_PIN) +#define IR_SEND_PIN 3 +#endif + +/* + * Specify DistanceWidthProtocol for decoding. This must be done before the #include + */ +#define DECODE_DISTANCE_WIDTH // Universal decoder for pulse distance width protocols +// +#if !defined(RAW_BUFFER_LENGTH) +// For air condition remotes it may require up to 750. Default is 200. +# if (defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF) +#define RAW_BUFFER_LENGTH 360 +# elif (defined(RAMEND) && RAMEND <= 0x8FF) || (defined(RAMSIZE) && RAMSIZE < 0x8FF) +#define RAW_BUFFER_LENGTH 750 +# endif +#endif + +//#define NO_LED_FEEDBACK_CODE // saves 92 bytes program memory +//#define RECORD_GAP_MICROS 12000 // Default is 8000. Activate it for some LG air conditioner protocols +//#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. +//#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition + +//#define DEBUG // Activate this for lots of lovely debug output from the decoders. + +#include + +#define SEND_BUTTON_PIN APPLICATION_PIN + +#define DELAY_BETWEEN_REPEATS_MILLIS 70 + +// Storage for the recorded code, pre-filled with NEC data +IRRawDataType sDecodedRawDataArray[DECODED_RAW_DATA_ARRAY_SIZE] = { 0x7B34ED12 }; // Initialize with NEC address 0x12 and command 0x34 +DistanceWidthTimingInfoStruct sDistanceWidthTimingInfo = { 9000, 4500, 560, 1690, 560, 560 }; // Initialize with NEC timing +uint8_t sNumberOfBits = 32; + +bool sSendButtonWasActive; + +void setup() { + pinMode(SEND_BUTTON_PIN, INPUT_PULLUP); + + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + Serial.println(F("Ready to receive pulse distance/width coded IR signals at pin " STR(IR_RECEIVE_PIN))); + + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin + Serial.print(F("Ready to send IR signals at pin " STR(IR_SEND_PIN) " on press of button at pin ")); + Serial.println(SEND_BUTTON_PIN); +} + +void loop() { + + // If button pressed, send the code. + bool tSendButtonIsActive = (digitalRead(SEND_BUTTON_PIN) == LOW); // Button pin is active LOW + + /* + * Check for current button state + */ + if (tSendButtonIsActive) { + if (!sSendButtonWasActive) { + Serial.println(F("Stop receiving")); + IrReceiver.stop(); + } + /* + * Button pressed -> send stored data + */ + Serial.print(F("Button pressed, now sending ")); + Serial.print(sNumberOfBits); + Serial.print(F(" bits 0x")); + Serial.print(sDecodedRawDataArray[0], HEX); + Serial.print(F(" with sendPulseDistanceWidthFromArray timing=")); + IrReceiver.printDistanceWidthTimingInfo(&Serial, &sDistanceWidthTimingInfo); + Serial.println(); + Serial.flush(); // To avoid disturbing the software PWM generation by serial output interrupts + + IrSender.sendPulseDistanceWidthFromArray(38, &sDistanceWidthTimingInfo, &sDecodedRawDataArray[0], sNumberOfBits, +#if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER) + PROTOCOL_IS_MSB_FIRST +#else + PROTOCOL_IS_LSB_FIRST +#endif + , 100, 0); + + delay(DELAY_BETWEEN_REPEATS_MILLIS); // Wait a bit between retransmissions + + } else if (sSendButtonWasActive) { + /* + * Button is just released -> activate receiving + */ + // Restart receiver + Serial.println(F("Button released -> start receiving")); + IrReceiver.start(); + + } else if (IrReceiver.decode()) { + /* + * Button is not pressed and data available -> store received data and resume + * DistanceWidthTimingInfo and sNumberOfBits should be constant for all keys of the same IR remote / protocol + */ + IrReceiver.printIRResultShort(&Serial); + if (IrReceiver.decodedIRData.protocol != UNKNOWN) { + IrReceiver.printIRSendUsage(&Serial); + + if (memcmp(&sDistanceWidthTimingInfo, &IrReceiver.decodedIRData.DistanceWidthTimingInfo, + sizeof(sDistanceWidthTimingInfo)) != 0) { + Serial.print(F("Store new timing info data=")); + IrReceiver.printDistanceWidthTimingInfo(&Serial, &IrReceiver.decodedIRData.DistanceWidthTimingInfo); + Serial.println(); + sDistanceWidthTimingInfo = IrReceiver.decodedIRData.DistanceWidthTimingInfo; // copy content here + } else { + Serial.print(F("Timing did not change, so we can reuse already stored timing info.")); + } + if (sNumberOfBits != IrReceiver.decodedIRData.numberOfBits) { + Serial.print(F("Store new numberOfBits=")); + sNumberOfBits = IrReceiver.decodedIRData.numberOfBits; + Serial.println(IrReceiver.decodedIRData.numberOfBits); + } + if (sDecodedRawDataArray[0] != IrReceiver.decodedIRData.decodedRawDataArray[0]) { + *sDecodedRawDataArray = *IrReceiver.decodedIRData.decodedRawDataArray; // copy content here + Serial.print(F("Store new sDecodedRawDataArray[0]=0x")); + Serial.println(IrReceiver.decodedIRData.decodedRawDataArray[0], HEX); + } + } + IrReceiver.resume(); // resume receiver + } + + sSendButtonWasActive = tSendButtonIsActive; + delay(100); +} diff --git a/examples/ReceiveAndSendHob2Hood/PinDefinitionsAndMore.h b/examples/ReceiveAndSendHob2Hood/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/ReceiveAndSendHob2Hood/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/ReceiveAndSendHob2Hood/ReceiveAndSendHob2Hood.ino b/examples/ReceiveAndSendHob2Hood/ReceiveAndSendHob2Hood.ino new file mode 100644 index 000000000..f9b3a2b09 --- /dev/null +++ b/examples/ReceiveAndSendHob2Hood/ReceiveAndSendHob2Hood.ino @@ -0,0 +1,145 @@ +/* + * ReceiveAndSendHob2Hood.cpp + * + * Demonstrates receiving and sending of IR codes for AEG / Elektrolux Hob2Hood protocol + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +#include + +#define DECODE_HASH // Only decoder, which works for Hob2Hood. protocol is UNKNOWN and only raw data is set. + +//#define NO_LED_FEEDBACK_CODE // saves 92 bytes program memory +//#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. +#include + +// IR commands from AEG hob2hood device +#define NUMBER_OF_HOB_TO_HOOD_COMMANDS 7 +#define HOB_TO_HOOD_HASH_CODE_FAN_1 0xE3C01BE2 +#define HOB_TO_HOOD_HASH_CODE_FAN_2 0xD051C301 +#define HOB_TO_HOOD_HASH_CODE_FAN_3 0xC22FFFD7 +#define HOB_TO_HOOD_HASH_CODE_FAN_4 0xB9121B29 +#define HOB_TO_HOOD_HASH_CODE_FAN_OFF 0x55303A3 +#define HOB_TO_HOOD_HASH_CODE_LIGHT_ON 0xE208293C +#define HOB_TO_HOOD_HASH_CODE_LIGHT_OFF 0x24ACF947 + +// based on https://pastebin.com/N6kG7Wu5 +#define HOB_TO_HOOD_UNIT_MICROS 725 +#define H2H_1 HOB_TO_HOOD_UNIT_MICROS +#define H2H_2 (HOB_TO_HOOD_UNIT_MICROS*2) // 1450 +#define H2H_3 (HOB_TO_HOOD_UNIT_MICROS*3) // 2175 +#define H2H_4 (HOB_TO_HOOD_UNIT_MICROS*4) // 2900 +#define H2H_5 (HOB_TO_HOOD_UNIT_MICROS*5) // 3625 + +// First entry is the length of the raw command +const uint16_t Fan1[] PROGMEM { 15, H2H_2, H2H_2, H2H_1, H2H_2, H2H_3, H2H_2, H2H_1, H2H_2, H2H_1, H2H_1, H2H_1, H2H_2, H2H_1, +H2H_3, H2H_1 }; +const uint16_t Fan2[] PROGMEM { 9, H2H_2, H2H_2, H2H_1, H2H_4, H2H_1, H2H_3, H2H_5, H2H_3, H2H_3 }; +const uint16_t Fan3[] PROGMEM { 9, H2H_1, H2H_3, H2H_4, H2H_4, H2H_3, H2H_1, H2H_1, H2H_3, H2H_3 }; +const uint16_t Fan4[] PROGMEM { 13, H2H_2, H2H_3, H2H_2, H2H_1, H2H_2, H2H_3, H2H_2, H2H_2, H2H_1, H2H_3, H2H_1, H2H_1, H2H_2 }; +const uint16_t FanOff[] PROGMEM { 15, H2H_1, H2H_2, H2H_1, H2H_2, H2H_3, H2H_2, H2H_1, H2H_2, H2H_2, H2H_3, H2H_1, H2H_2, H2H_1, +H2H_1, H2H_1 }; +const uint16_t LightOn[] PROGMEM { 17, H2H_1, H2H_2, H2H_1, H2H_1, H2H_2, H2H_1, H2H_1, H2H_2, H2H_1, H2H_1, H2H_2, H2H_4, H2H_1, +H2H_1, H2H_1, H2H_1, H2H_2 }; +const uint16_t LightOff[] PROGMEM { 17, H2H_1, H2H_2, H2H_1, H2H_1, H2H_1, H2H_1, H2H_1, H2H_3, H2H_1, H2H_1, H2H_1, H2H_2, H2H_1, +H2H_2, H2H_1, H2H_1, H2H_1 }; +const uint16_t *const Hob2HoodSendCommands[NUMBER_OF_HOB_TO_HOOD_COMMANDS] = { Fan1, Fan2, Fan3, Fan4, FanOff, LightOn, LightOff }; // Constant array in RAM + +void setup() { + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.print(F("Ready to receive Hob2Hood IR signals at pin " STR(IR_RECEIVE_PIN))); + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin + Serial.println(F("Send Hob2Hood IR signals at pin " STR(IR_SEND_PIN))); +} + +/* + * Receive and send Hob2Hood protocol + */ +void loop() { + static long sLastMillisOfSend = 0; + static uint8_t sSendCommandIndex = 0; + + if (IrReceiver.decode()) { + IrReceiver.resume(); // Early enable receiving of the next IR frame + IrReceiver.printIRResultShort(&Serial); + + /* + * Finally, check the received data and perform actions according to the received command + */ + switch (IrReceiver.decodedIRData.decodedRawData) { + case HOB_TO_HOOD_HASH_CODE_FAN_OFF: + Serial.print(F("FAN off")); + break; + case HOB_TO_HOOD_HASH_CODE_FAN_1: + Serial.print(F("FAN 1")); + break; + case HOB_TO_HOOD_HASH_CODE_FAN_2: + Serial.print(F("FAN 2")); + break; + default: + Serial.print(F("unknown Hob2Hood IR command")); + break; + } + } + + /* + * Send next command every 5 seconds + */ + if (millis() - sLastMillisOfSend > 2000) { + sLastMillisOfSend = millis(); + +#if defined(__AVR__) + uint16_t tLengthOfRawCommand = pgm_read_word(Hob2HoodSendCommands[sSendCommandIndex]); // length is the 1. word in array +#else + uint16_t tLengthOfRawCommand = *Hob2HoodSendCommands[sSendCommandIndex]; // length is the 1. word in array +#endif + const uint16_t *tAddressOfRawCommandSequence = Hob2HoodSendCommands[sSendCommandIndex] + 1; // Raw sequence starts at the 2. word of array + Serial.print(F("Send Hob2Hood command index=")); + Serial.println(sSendCommandIndex); + IrSender.sendRaw_P(tAddressOfRawCommandSequence, tLengthOfRawCommand, 38); + + // Prepare for next command + sSendCommandIndex++; + if (sSendCommandIndex >= NUMBER_OF_HOB_TO_HOOD_COMMANDS) { + sSendCommandIndex = 0; + } + } +} diff --git a/examples/ReceiveDemo/IRremote_SendDemo_ReceiveDemo.log b/examples/ReceiveDemo/IRremote_SendDemo_ReceiveDemo.log new file mode 100644 index 000000000..d56a9481f --- /dev/null +++ b/examples/ReceiveDemo/IRremote_SendDemo_ReceiveDemo.log @@ -0,0 +1,178 @@ +START ../src/ReceiveDemo.cpp from Feb 24 2023 +Using library version 4.1.0 +Enabling IRin... +Ready to receive IR signals of protocols: NEC/NEC2/Onkyo/Apple, Panasonic/Kaseikyo, Denon/Sharp, Sony, RC5, RC6, LG, JVC, Samsung, FAST, Whynter, Lego Power Functions, Bosewave , MagiQuest, Universal Pulse Distance Width, Hash at pin 2 + +If you connect debug pin 5 to ground, raw data is always printed +5000 us is the (minimum) gap, after which the start of a new IR packet is assumed +20 us are subtracted from all marks and added to all spaces for decoding + +Protocol=NEC Address=0x2 Command=0x34 Raw-Data=0xCB34FD02 32 bits LSB first +Send with: IrSender.sendNEC(0x2, 0x34, ); + +Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendNEC(0x102, 0x34, ); + +Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendNEC(0x102, 0x34, ); + +Protocol=NEC Address=0x80 Command=0x45 Raw-Data=0xBA457F80 32 bits LSB first +Send with: IrSender.sendNEC(0x80, 0x45, ); + +Protocol=NEC Address=0x4 Command=0x8 Raw-Data=0xF708FB04 32 bits LSB first +Send with: IrSender.sendNEC(0x4, 0x8, ); + +Protocol=Onkyo Address=0x102 Command=0x304 Raw-Data=0x3040102 32 bits LSB first +Send with: IrSender.sendOnkyo(0x102, 0x304, ); + +Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendNEC(0x102, 0x34, ); + +Protocol=Panasonic Address=0xB Command=0x10 Raw-Data=0xA01000B0 48 bits LSB first +Send with: IrSender.sendPanasonic(0xB, 0x10, ); + +Protocol=Panasonic Address=0xB Command=0x10 Raw-Data=0xA01000B0 48 bits LSB first +Send with: IrSender.sendPanasonic(0xB, 0x10, ); + +Protocol=Panasonic Address=0xB Command=0x10 Raw-Data=0xA01000B0 48 bits LSB first +Send with: IrSender.sendPanasonic(0xB, 0x10, ); + +Protocol=PulseDistance Raw-Data=0x5A 72 bits LSB first +Send with: + uint32_t tRawData[]={0x87654321, 0xAFEDCBA9, 0x5A}; + IrSender.sendPulseDistanceWidthFromArray(38, 8850, 4400, 550, 1700, 550, 600, &tRawData[0], 72, PROTOCOL_IS_LSB_FIRST, , ); + +Protocol=PulseWidth Raw-Data=0xDCBA9 52 bits LSB first +Send with: + uint32_t tRawData[]={0x87654321, 0xDCBA9}; + IrSender.sendPulseDistanceWidthFromArray(38, 300, 600, 600, 300, 350, 600, &tRawData[0], 52, PROTOCOL_IS_LSB_FIRST, , ); + +Protocol=PulseWidth Raw-Data=0x87654321 32 bits LSB first +Send with: IrSender.sendPulseDistanceWidth(38, 1000, 500, 600, 300, 350, 300, 0x87654321, 32, PROTOCOL_IS_LSB_FIRST, , ); + +Protocol=Onkyo Address=0x102 Command=0x5634 Raw-Data=0x56340102 32 bits LSB first +Send with: IrSender.sendOnkyo(0x102, 0x5634, ); + +Protocol=Apple Address=0x2 Command=0x34 Raw-Data=0x23487EE 32 bits LSB first +Send with: IrSender.sendApple(0x2, 0x34, ); + +Protocol=Panasonic Address=0x102 Command=0x34 Raw-Data=0x4341020 48 bits LSB first +Send with: IrSender.sendPanasonic(0x102, 0x34, ); + +Protocol=Kaseikyo Address=0x102 Command=0x34 Extra=0x4711 Raw-Data=0x7341023 48 bits LSB first +Send with: IrSender.sendKaseikyo(0x102, 0x34, , 0x4711); + +Protocol=Kaseikyo_Denon Address=0x102 Command=0x34 Raw-Data=0x4341020 48 bits LSB first +Send with: IrSender.sendKaseikyo_Denon(0x102, 0x34, ); + +Protocol=Denon Address=0x2 Command=0x34 Raw-Data=0x682 15 bits LSB first +Send with: IrSender.sendDenon(0x2, 0x34, ); + +Protocol=Denon Address=0x2 Command=0x34 Auto-Repeat gap=45650us Raw-Data=0x7962 15 bits LSB first + +Protocol=Sharp Address=0x2 Command=0x34 Raw-Data=0x4682 15 bits LSB first +Send with: IrSender.sendSharp(0x2, 0x34, ); + +Protocol=Sharp Address=0x2 Command=0x34 Auto-Repeat gap=46400us Raw-Data=0x3962 15 bits LSB first + +Protocol=Sony Address=0x2 Command=0x34 Raw-Data=0x134 12 bits LSB first +Send with: IrSender.sendSony(0x2, 0x34, 2, 12); + +Protocol=Sony Address=0x2 Command=0x34 Raw-Data=0x134 15 bits LSB first +Send with: IrSender.sendSony(0x2, 0x34, 2, 15); + +Protocol=Sony Address=0x102 Command=0x34 Raw-Data=0x8134 20 bits LSB first +Send with: IrSender.sendSony(0x102, 0x34, 2, 20); + +Protocol=Samsung Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendSamsung(0x102, 0x34, ); + +Protocol=Samsung Address=0x102 Command=0x5634 Raw-Data=0x56340102 32 bits LSB first +Send with: IrSender.sendSamsung(0x102, 0x5634, ); + +Protocol=Samsung48 Address=0x102 Command=0x5634 Raw-Data=0xA956 48 bits LSB first +Send with: IrSender.sendSamsung48(0x102, 0x5634, ); + +Protocol=RC5 Address=0x2 Command=0x34 Raw-Data=0x10B4 13 bits MSB first +Send with: IrSender.sendRC5(0x2, 0x34, ); + +Protocol=RC5 Address=0x2 Command=0x74 Toggle=1 Raw-Data=0x8B4 13 bits MSB first +Send with: IrSender.sendRC5(0x2, 0x74, ); + +Protocol=RC6 Address=0x2 Command=0x34 Raw-Data=0x234 20 bits MSB first +Send with: IrSender.sendRC6(0x2, 0x34, ); + +Protocol=Samsung Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendSamsung(0x102, 0x34, ); + +Protocol=JVC Address=0x2 Command=0x34 Raw-Data=0x3402 16 bits LSB first +Send with: IrSender.sendJVC(0x2, 0x34, ); + +Protocol=Samsung Address=0x102 Command=0x5634 Raw-Data=0x56340102 32 bits LSB first +Send with: IrSender.sendSamsung(0x102, 0x5634, ); + +Protocol=LG Address=0x2 Command=0x5634 Raw-Data=0x256342 28 bits MSB first +Send with: IrSender.sendLG(0x2, 0x5634, ); + +Protocol=MagiQuest Address=0x102 Command=0x34 Raw-Data=0x6BCD0102 56 bits MSB first +Send with: IrSender.sendMagiQuest(0x6BCD0102, 0x34, ); + +Protocol=BoseWave Address=0x0 Command=0x34 Raw-Data=0xCB34 16 bits LSB first +Send with: IrSender.sendBoseWave(0x0, 0x34, ); + +Protocol=FAST Address=0x0 Command=0x34 Raw-Data=0xCB34 16 bits LSB first +Send with: IrSender.sendFAST(0x0, 0x34, ); + +Protocol=Lego Address=0x2 Command=0x14 Raw-Data=0x2148 16 bits MSB first +Send with: IrSender.sendLego(0x2, 0x14, ); + +Protocol=Lego Address=0x2 Command=0x14 Auto-Repeat gap=180450us Raw-Data=0x2148 16 bits MSB first + +Protocol=Lego Address=0x2 Command=0x14 Auto-Repeat gap=179350us Raw-Data=0x2148 16 bits MSB first + +Protocol=Lego Address=0x2 Command=0x14 Auto-Repeat gap=179200us Raw-Data=0x2148 16 bits MSB first + +Protocol=Lego Address=0x2 Command=0x14 Auto-Repeat gap=179150us Raw-Data=0x2148 16 bits MSB first + +Overflow detected +Try to increase the "RAW_BUFFER_LENGTH" value of 600 in ../src/ReceiveDemo.cpp + +Protocol=NEC Address=0x3 Command=0x45 Raw-Data=0xBA45FC03 32 bits LSB first +Send with: IrSender.sendNEC(0x3, 0x45, ); + +Protocol=NEC Address=0x3 Command=0x45 Repeat gap=43250us + +Protocol=NEC Address=0x203 Command=0x45 Raw-Data=0xBA450203 32 bits LSB first +Send with: IrSender.sendNEC(0x203, 0x45, ); + +Protocol=NEC Address=0x203 Command=0x45 Repeat gap=47550us + +Protocol=NEC Address=0x203 Command=0x45 Raw-Data=0xBA450203 32 bits LSB first +Send with: IrSender.sendNEC(0x203, 0x45, ); + +Protocol=NEC2 Address=0x203 Command=0x45 Repeat gap=46500us Raw-Data=0xBA450203 32 bits LSB first + +Protocol=Onkyo Address=0x203 Command=0x6745 Raw-Data=0x67450203 32 bits LSB first +Send with: IrSender.sendOnkyo(0x203, 0x6745, ); + +Protocol=Onkyo Address=0x203 Command=0x6745 Repeat gap=46550us + +Protocol=Apple Address=0x3 Command=0x45 Raw-Data=0x34587EE 32 bits LSB first +Send with: IrSender.sendApple(0x3, 0x45, ); + +Protocol=Apple Address=0x3 Command=0x45 Repeat gap=31550us + +Protocol=Panasonic Address=0x203 Command=0x45 Raw-Data=0x55452030 48 bits LSB first +Send with: IrSender.sendPanasonic(0x203, 0x45, ); + +Protocol=Panasonic Address=0x203 Command=0x45 Repeat gap=72550us Raw-Data=0x55452030 48 bits LSB first + +Protocol=Kaseikyo Address=0x203 Command=0x45 Extra=0x4711 Raw-Data=0x56452033 48 bits LSB first +Send with: IrSender.sendKaseikyo(0x203, 0x45, , 0x4711); + +Protocol=Kaseikyo Address=0x203 Command=0x45 Extra=0x4711 Repeat gap=66750us Raw-Data=0x56452033 48 bits LSB first + +Protocol=Kaseikyo_Denon Address=0x203 Command=0x45 Raw-Data=0x55452030 48 bits LSB first +Send with: IrSender.sendKaseikyo_Denon(0x203, 0x45, ); + +Protocol=Kaseikyo_Denon Address=0x203 Command=0x45 Repeat gap=68300us Raw-Data=0x55452030 48 bits LSB first \ No newline at end of file diff --git a/examples/ReceiveDemo/PinDefinitionsAndMore.h b/examples/ReceiveDemo/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/ReceiveDemo/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/ReceiveDemo/ReceiveDemo.ino b/examples/ReceiveDemo/ReceiveDemo.ino new file mode 100644 index 000000000..e7aa1eb7b --- /dev/null +++ b/examples/ReceiveDemo/ReceiveDemo.ino @@ -0,0 +1,361 @@ +/* + * ReceiveDemo.cpp + * + * Demonstrates receiving IR codes with the IRremote library and the use of the Arduino tone() function with this library. + * Long press of one IR button (receiving of multiple repeats for one command) is detected. + * If debug button is pressed (pin connected to ground) a long output is generated, which may disturb detecting of repeats. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2025 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. + +//#define LOCAL_DEBUG // If defined, print timing for each received data set (the same as if DEBUG_BUTTON_PIN was connected to low) + +/* + * Specify which protocol(s) should be used for decoding. + * If no protocol is defined, all protocols (except Bang&Olufsen) are active. + * This must be done before the #include + */ +//#define DECODE_DENON // Includes Sharp +//#define DECODE_JVC +//#define DECODE_KASEIKYO +//#define DECODE_PANASONIC // alias for DECODE_KASEIKYO +//#define DECODE_LG +//#define DECODE_ONKYO // Decodes only Onkyo and not NEC or Apple +//#define DECODE_NEC // Includes Apple and Onkyo +//#define DECODE_SAMSUNG +//#define DECODE_SONY +//#define DECODE_RC5 +//#define DECODE_RC6 +//#define DECODE_BOSEWAVE +//#define DECODE_LEGO_PF +//#define DECODE_MAGIQUEST +//#define DECODE_WHYNTER +//#define DECODE_FAST +//#define DECODE_DISTANCE_WIDTH // Universal decoder for pulse distance width protocols +//#define DECODE_HASH // special decoder for all protocols +//#define DECODE_BEO // This protocol must always be enabled manually, i.e. it is NOT enabled if no protocol is defined. It prevents decoding of SONY! +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604. Code does not fit in program memory of ATtiny85 etc. +// !!! Enabling B&O disables detection of Sony, because the repeat gap for SONY is smaller than the B&O frame gap :-( !!! +//#define DECODE_BEO // Bang & Olufsen protocol always must be enabled explicitly. It has an IR transmit frequency of 455 kHz! It prevents decoding of SONY! +#else +// for 8k flash +//#define DECODE_DENON // Includes Sharp +#define DECODE_JVC +#define DECODE_KASEIKYO +#define DECODE_PANASONIC // alias for DECODE_KASEIKYO +#define DECODE_LG +#define DECODE_NEC // Includes Apple and Onkyo +#define DECODE_SAMSUNG +//#define DECODE_SONY +//#define DECODE_RC5 +//#define DECODE_RC6 +#define DECODE_DISTANCE_WIDTH // Universal decoder for pulse distance width protocols +#define DECODE_HASH // special decoder for all protocols + +#define EXCLUDE_EXOTIC_PROTOCOLS +#endif +// etc. see IRremote.hpp +// + +#if !defined(RAW_BUFFER_LENGTH) +// For air condition remotes it may require up to 750. Default is 200. +# if !((defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF)) +#define RAW_BUFFER_LENGTH 750 +# endif +#endif + +//#define NO_LED_FEEDBACK_CODE // saves 92 bytes program memory +//#define EXCLUDE_UNIVERSAL_PROTOCOLS // Saves up to 1000 bytes program memory. +//#define EXCLUDE_EXOTIC_PROTOCOLS // saves around 650 bytes program memory if all other protocols are active +//#define IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK // saves 32 bytes program memory +// MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding, +// to compensate for the signal forming of different IR receiver modules. See also IRremote.hpp line 142. +//#define MARK_EXCESS_MICROS 20 // Adapt it to your IR receiver module. 40 is taken for the cheap VS1838 module her, since we have high intensity. + +#if defined(DECODE_BEO) +#define RECORD_GAP_MICROS 16000 // always get the complete frame in the receive buffer, but this prevents decoding of SONY! +#endif +//#define RECORD_GAP_MICROS 12000 // Default is 8000. Activate it for some LG air conditioner protocols + +//#define DEBUG // Activate this for lots of lovely debug output from the decoders. + +#include + +#if defined(APPLICATION_PIN) +#define DEBUG_BUTTON_PIN APPLICATION_PIN // if low, print timing for each received data set +#else +#define DEBUG_BUTTON_PIN 6 +#endif +#if defined(ESP32) +# if !digitalPinIsValid(DEBUG_BUTTON_PIN) +#undef DEBUG_BUTTON_PIN // DEBUG_BUTTON_PIN number is not valid, so delete definition to disable further usage +# endif +#endif + +void generateTone(); +void handleOverflow(); +bool detectLongPress(uint16_t aLongPressDurationMillis); + +void setup() { +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604. Code does not fit in program memory of ATtiny85 etc. +# if defined(DEBUG_BUTTON_PIN) + pinMode(DEBUG_BUTTON_PIN, INPUT_PULLUP); +# endif +#endif + + Serial.begin(9600); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) || (defined(ESP32) && defined(ARDUINO_USB_MODE)) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + +// Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + +// In case the interrupt driver crashes on setup, give a clue +// to the user what's going on. + Serial.println(F("Enabling IRin...")); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); +#if defined(IR_RECEIVE_PIN_STRING) + Serial.println(F("at pin " IR_RECEIVE_PIN_STRING)); +#else + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); +#endif + +#if defined(LED_BUILTIN) && !defined(NO_LED_FEEDBACK_CODE) +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + Serial.print(F("Active low ")); +# endif + Serial.print(F("FeedbackLED at pin ")); + Serial.println(LED_BUILTIN); // Works also for ESP32: static const uint8_t LED_BUILTIN = 8; #define LED_BUILTIN LED_BUILTIN +#endif + +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604. Code does not fit in program memory of ATtiny85 etc. + Serial.println(); +# if defined(DEBUG_BUTTON_PIN) + Serial.print(F("If you connect debug pin ")); +# if defined(APPLICATION_PIN_STRING) + Serial.print(APPLICATION_PIN_STRING); +# else + Serial.print(DEBUG_BUTTON_PIN); +# endif + Serial.println(F(" to ground, raw data is always printed and tone is disabled")); +# endif + + // infos for receive + Serial.print(RECORD_GAP_MICROS); + Serial.println(F(" us is the (minimum) gap, after which the start of a new IR packet is assumed")); + Serial.print(MARK_EXCESS_MICROS); + Serial.println(F(" us are subtracted from all marks and added to all spaces for decoding")); +#endif // FLASHEND >= 0x3FFF +} + +void loop() { + /* + * Check if received data is available and if yes, try to decode it. + * Decoded result is in the IrReceiver.decodedIRData structure. + * + * E.g. command is in IrReceiver.decodedIRData.command + * address is in command is in IrReceiver.decodedIRData.address + * and up to 32 bit raw data in IrReceiver.decodedIRData.decodedRawData + */ + if (IrReceiver.decode()) { + Serial.println(); +#if FLASHEND < 0x3FFF // For less than 16k flash, only print a minimal summary of received data + IrReceiver.printIRResultMinimal(&Serial); +#else + + /* + * + */ + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) { + handleOverflow(); + } else { + /* + * No overflow here. + * Stop receiver, generate a single beep, print short info and send usage and start receiver again + */ + if ((IrReceiver.decodedIRData.protocol != SONY) && (IrReceiver.decodedIRData.protocol != PULSE_WIDTH) + && (IrReceiver.decodedIRData.protocol != PULSE_DISTANCE) && (IrReceiver.decodedIRData.protocol != UNKNOWN) +#if defined(DEBUG_BUTTON_PIN) + && digitalRead(DEBUG_BUTTON_PIN) != LOW +#endif + ) { + /* + * For SONY the tone prevents the detection of a repeat after the 15 ms SONY gap. + * In debug mode and for unknown protocols, we need the time for extended output. + * Skipping tone will get exact gap time between transmissions and not running into repeat frames while wait for tone to end. + * This in turn enables the next CheckForRecordGapsMicros() call a chance to eventually propose a change of the current RECORD_GAP_MICROS value. + */ + generateTone(); + } + + /* + * Print info + */ + if (IrReceiver.decodedIRData.protocol == UNKNOWN +#if defined(DEBUG_BUTTON_PIN) + || digitalRead(DEBUG_BUTTON_PIN) == LOW) +#endif + { + // We have debug enabled or an unknown protocol, print extended info + if (IrReceiver.decodedIRData.protocol == UNKNOWN) { + Serial.println(F("Received noise or an unknown (or not yet enabled) protocol")); + } + IrReceiver.printIRResultRawFormatted(&Serial, true); + } + if (IrReceiver.decodedIRData.protocol == UNKNOWN) { + auto tDecodedRawData = IrReceiver.decodedIRData.decodedRawData; // uint32_t on 8 and 16 bit CPUs and uint64_t on 32 and 64 bit CPUs + Serial.print(F("Raw data received are 0x")); + Serial.println(tDecodedRawData); + + } else { + /* + * The info output for a successful receive + */ + IrReceiver.printIRResultShort(&Serial); + IrReceiver.printIRSendUsage(&Serial); + } + } +#endif // #if FLASHEND >= 0x3FFF + + /* + * !!!Important!!! Enable receiving of the next value, because receiving + * has stopped after the end of the current received data packet. + * Do it here, to preserve raw data for printing with printIRResultRawFormatted() + */ + IrReceiver.resume(); + + /* + * Finally check the received data and perform actions according to the received address and commands + */ + + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) { + Serial.println(F("Repeat received. Here you can repeat the same action as before.")); + } else { + if (IrReceiver.decodedIRData.address == 0) { + if (IrReceiver.decodedIRData.command == 0x10) { + // do something + } else if (IrReceiver.decodedIRData.command == 0x11) { + // do something else + } + } + } + + // Check if repeats of the IR command was sent for more than 1000 ms + if (detectLongPress(1000)) { + Serial.print(F("Command 0x")); + Serial.print(IrReceiver.decodedIRData.command, HEX); + Serial.println(F(" was repeated for more than 2 seconds")); + } + } // if (IrReceiver.decode()) + + /* + * Your code here + * For all users of the FastLed library, use this code for strip.show() to improve receiving performance (which is still not 100%): + * if (IrReceiver.isIdle()) { + * strip.show(); + * } + */ + +} + +#if FLASHEND >= 0x3FFF // No tone() available when using ATTinyCore +/* + * Stop receiver, generate a single beep and start receiver again + */ +void generateTone() { +# if !defined(ESP8266) && !defined(NRF5) // tone on esp8266 works only once, then it disables IrReceiver.restartTimer() / timerConfigForReceive(). +# if defined(ESP32) // ESP32 uses another timer for tone(), maybe other platforms (not tested yet) too. + tone(TONE_PIN, 2200, 8); +# else + IrReceiver.stopTimer(); // Stop timer consistently before calling tone() or other functions using the timer resource. + tone(TONE_PIN, 2200, 8); + delay(8); + IrReceiver.restartTimer(); // Restart IR timer after timer resource is no longer blocked. +# endif +# endif +} +#endif // FLASHEND >= 0x3FFF + +void handleOverflow() { + Serial.println(F("Overflow detected")); + Serial.println(F("Try to increase the \"RAW_BUFFER_LENGTH\" value of " STR(RAW_BUFFER_LENGTH) " in " __FILE__)); + // see also https://github.com/Arduino-IRremote/Arduino-IRremote#compile-options--macros-for-this-library + +#if !defined(ESP8266) && !defined(NRF5) && FLASHEND >= 0x3FFF // tone on esp8266 works once, then it disables IrReceiver.restartTimer() / timerConfigForReceive(). + /* + * Stop timer, generate a double beep and start timer again + */ +# if defined(ESP32) // ESP32 uses another timer for tone() + tone(TONE_PIN, 1100, 10); + delay(50); + tone(TONE_PIN, 1100, 10); +# else + IrReceiver.stopTimer(); + tone(TONE_PIN, 1100, 10); + delay(50); + tone(TONE_PIN, 1100, 10); + delay(50); + IrReceiver.restartTimer(); +# endif +#endif +} + +unsigned long sMillisOfFirstReceive; +bool sLongPressJustDetected; +/** + * True once we received the consecutive repeats for more than aLongPressDurationMillis milliseconds. + * The first frame, which is no repeat, is NOT counted for the duration! + * @return true once after the repeated IR command was received for longer than aLongPressDurationMillis milliseconds, false otherwise. + */ +bool detectLongPress(uint16_t aLongPressDurationMillis) { + if (!sLongPressJustDetected && (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT)) { + /* + * Here the repeat flag is set (which implies, that command is the same as the previous one) + */ + if (millis() - aLongPressDurationMillis > sMillisOfFirstReceive) { + sLongPressJustDetected = true; // Long press here + } + } else { + // No repeat here + sMillisOfFirstReceive = millis(); + sLongPressJustDetected = false; + } + return sLongPressJustDetected; // No long press here +} + diff --git a/examples/ReceiveDump/PinDefinitionsAndMore.h b/examples/ReceiveDump/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/ReceiveDump/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/ReceiveDump/ReceiveDump.ino b/examples/ReceiveDump/ReceiveDump.ino new file mode 100644 index 000000000..b01010626 --- /dev/null +++ b/examples/ReceiveDump/ReceiveDump.ino @@ -0,0 +1,160 @@ +/* + * ReceiveDump.cpp + * + * Dumps the received signal in different flavors. + * Since the printing takes so much time (200 ms @115200 for NEC protocol, 70ms for NEC repeat), + * repeat signals may be skipped or interpreted as UNKNOWN. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. + +#if !defined(RAW_BUFFER_LENGTH) +// For air condition remotes it may require up to 750. Default is 200. +# if !((defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF)) +#define RAW_BUFFER_LENGTH 730 // this allows usage of 16 bit raw buffer, for RECORD_GAP_MICROS > 20000 +# endif +#endif + +/* + * MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding, + * to compensate for the signal forming of different IR receiver modules. See also IRremote.hpp line 142. + * + * You can change this value accordingly to the receiver module you use. + * The required value can be derived from the timings printed here. + * Keep in mind that the timings may change with the distance + * between sender and receiver as well as with the ambient light intensity. + */ +#define MARK_EXCESS_MICROS 20 // Adapt it to your IR receiver module. 20 is recommended for the cheap VS1838 modules. + +//#define RECORD_GAP_MICROS 12000 // Default is 8000. Activate it for some LG air conditioner protocols +//#define DEBUG // Activate this for lots of lovely debug output from the decoders. + +#include + +//+============================================================================= +// Configure the Arduino +// +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); // Status message will be sent to PC at 9600 baud + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + // Wait until Serial Monitor is attached. + // Required for boards using USB code for Serial like Leonardo. + // Is void for USB Serial implementations using external chips e.g. a CH340. + while (!Serial) + ; + // !!! Program will not proceed if no Serial Monitor is attached !!! +#endif + + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); + + // infos for receive + Serial.print(RECORD_GAP_MICROS); + Serial.println(F(" us is the (minimum) gap, after which the start of a new IR packet is assumed")); + Serial.print(MARK_EXCESS_MICROS); + Serial.println(); + Serial.println(F("Because of the verbose output (>200 ms at 115200 baud), repeats are not dumped correctly!")); + Serial.println(); + Serial.println( + F( + "If you receive protocol NEC, Samsung or LG, run also ReceiveDemo to check if your actual protocol is eventually NEC2 or SamsungLG, which is determined by the repeats")); + Serial.println(); + +} + +//+============================================================================= +// The repeating section of the code +// +void loop() { + if (IrReceiver.decode()) { // Grab an IR code + // At 115200 baud, printing takes 200 ms for NEC protocol and 70 ms for NEC repeat + Serial.println(); // blank line between entries + Serial.println(); // 2 blank lines between entries + IrReceiver.printIRResultShort(&Serial); + // Check if the buffer overflowed + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) { + Serial.println(F("Try to increase the \"RAW_BUFFER_LENGTH\" value of " STR(RAW_BUFFER_LENGTH) " in " __FILE__)); + // see also https://github.com/Arduino-IRremote/Arduino-IRremote#compile-options--macros-for-this-library + } else { + if (IrReceiver.decodedIRData.protocol == UNKNOWN) { + Serial.println(F("Received noise or an unknown (or not yet enabled) protocol")); + } + Serial.println(); + IrReceiver.printIRSendUsage(&Serial); + Serial.println(); + Serial.println(F("Raw result in internal ticks (50 us) - with leading gap")); + IrReceiver.printIRResultRawFormatted(&Serial, false); // Output the results in RAW format + Serial.println(F("Raw result in microseconds - with leading gap")); + IrReceiver.printIRResultRawFormatted(&Serial, true); // Output the results in RAW format + Serial.println(); // blank line between entries + Serial.print(F("Result as internal 8bit ticks (50 us) array - compensated with MARK_EXCESS_MICROS=")); + Serial.println(MARK_EXCESS_MICROS); + IrReceiver.compensateAndPrintIRResultAsCArray(&Serial, false); // Output the results as uint8_t source code array of ticks + Serial.print(F("Result as microseconds array - compensated with MARK_EXCESS_MICROS=")); + Serial.println(MARK_EXCESS_MICROS); + IrReceiver.compensateAndPrintIRResultAsCArray(&Serial, true); // Output the results as uint16_t source code array of micros + IrReceiver.printIRResultAsCVariables(&Serial); // Output address and data as source code variables + Serial.println(); // blank line between entries + + IrReceiver.compensateAndPrintIRResultAsPronto(&Serial); + + /* + * Example for using the compensateAndStorePronto() function. + * Creating this String requires 2210 bytes program memory and 10 bytes RAM for the String class. + * The String object itself requires additional 440 bytes RAM from the heap. + * This values are for an Arduino Uno. + */ +// Serial.println(); // blank line between entries +// String ProntoHEX = F("Pronto HEX contains: "); // Assign string to ProtoHex string object +// if (int size = IrReceiver.compensateAndStorePronto(&ProntoHEX)) { // Dump the content of the IReceiver Pronto HEX to the String object +// // Append compensateAndStorePronto() size information to the String object (requires 50 bytes heap) +// ProntoHEX += F("\r\nProntoHEX is "); // Add codes size information to the String object +// ProntoHEX += size; +// ProntoHEX += F(" characters long and contains "); // Add codes count information to the String object +// ProntoHEX += size / 5; +// ProntoHEX += F(" codes"); +// Serial.println(ProntoHEX.c_str()); // Print to the serial console the whole String object +// Serial.println(); // blank line between entries +// } + } + IrReceiver.resume(); // Prepare for the next IR frame + } +} diff --git a/examples/ReceiveDump/ReceiveDump.log b/examples/ReceiveDump/ReceiveDump.log new file mode 100644 index 000000000..ae16f33e9 --- /dev/null +++ b/examples/ReceiveDump/ReceiveDump.log @@ -0,0 +1,52 @@ +START ../src/ReceiveDump.cpp from Nov 12 2022 +Using library version 4.0.0 +Ready to receive IR signals of protocols: NEC/NEC2/Onkyo/Apple, Panasonic/Kaseikyo, Denon/Sharp, Sony, RC5, RC6, LG, JVC, Samsung, Whynter, Lego Power Functions, Bosewave , MagiQuest, Universal Pulse Distance Width, Hash at pin 2 +5000 us is the (minimum) gap, after which the start of a new IR packet is assumed +20 us are subtracted from all marks and added to all spaces for decoding + + +Protocol=Samsung Address=0x707 Command=0x4 Raw-Data=0xFB040707 32 bits LSB first + +Send with: IrSender.sendSamsung(0x707, 0x4, ); + +Raw result in internal ticks (50 us) - with leading gap +rawData[68]: + -27948 + +90,-84 + +12,-32 +12,-32 +12,-32 +12,-11 + +11,-11 +11,-11 +11,-11 +11,-11 + +12,-32 +12,-32 +12,-32 +12,-10 + +12,-10 +12,-10 +12,-10 +12,-11 + +11,-11 +11,-11 +11,-33 +11,-11 + +11,-11 +11,-11 +11,-11 +11,-11 + +12,-32 +12,-32 +12,-10 +12,-32 + +12,-32 +12,-32 +12,-32 +12,-32 + +12 +Sum: 1200 +Raw result in microseconds - with leading gap +rawData[68]: + -1397400 + +4500,-4200 + + 600,-1600 + 600,-1600 + 600,-1600 + 600,- 550 + + 550,- 550 + 550,- 550 + 550,- 550 + 550,- 550 + + 600,-1600 + 600,-1600 + 600,-1600 + 600,- 500 + + 600,- 500 + 600,- 500 + 600,- 500 + 600,- 550 + + 550,- 550 + 550,- 550 + 550,-1650 + 550,- 550 + + 550,- 550 + 550,- 550 + 550,- 550 + 550,- 550 + + 600,-1600 + 600,-1600 + 600,- 500 + 600,-1600 + + 600,-1600 + 600,-1600 + 600,-1600 + 600,-1600 + + 600 +Sum: 60000 + +Result as internal ticks (50 us) array - compensated with MARK_EXCESS_MICROS=20 +uint8_t rawTicks[67] = {90,84, 12,32, 12,32, 12,32, 12,11, 11,11, 11,11, 11,11, 11,11, 12,32, 12,32, 12,32, 12,10, 12,10, 12,10, 12,10, 12,11, 11,11, 11,11, 11,33, 11,11, 11,11, 11,11, 11,11, 11,11, 12,32, 12,32, 12,10, 12,32, 12,32, 12,32, 12,32, 12,32, 12}; // Protocol=Samsung Address=0x707 Command=0x4 Raw-Data=0xFB040707 32 bits LSB first + +Result as microseconds array - compensated with MARK_EXCESS_MICROS=20 +uint16_t rawData[67] = {4480,4220, 580,1620, 580,1620, 580,1620, 580,570, 530,570, 530,570, 530,570, 530,570, 580,1620, 580,1620, 580,1620, 580,520, 580,520, 580,520, 580,520, 580,570, 530,570, 530,570, 530,1670, 530,570, 530,570, 530,570, 530,570, 530,570, 580,1620, 580,1620, 580,520, 580,1620, 580,1620, 580,1620, 580,1620, 580,1620, 580}; // Protocol=Samsung Address=0x707 Command=0x4 Raw-Data=0xFB040707 32 bits LSB first + +uint16_t address = 0x707; +uint16_t command = 0x4; +uint32_t data = 0xFB040707; + +Pronto Hex as string +char prontoData[] = "0000 006D 0022 0000 00AE 00A1 0018 003D 0018 003D 0018 003D 0018 0014 0016 0014 0016 0014 0016 0014 0016 0014 0018 003D 0018 003D 0018 003D 0018 0012 0018 0012 0018 0012 0018 0012 0018 0014 0016 0014 0016 0014 0016 003F 0016 0014 0016 0014 0016 0014 0016 0014 0016 0014 0018 003D 0018 003D 0018 0012 0018 003D 0018 003D 0018 003D 0018 003D 0018 003D 0018 06C3 "; diff --git a/examples/ReceiveOneAndSendMultiple/PinDefinitionsAndMore.h b/examples/ReceiveOneAndSendMultiple/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/ReceiveOneAndSendMultiple/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/ReceiveOneAndSendMultiple/ReceiveOneAndSendMultiple.ino b/examples/ReceiveOneAndSendMultiple/ReceiveOneAndSendMultiple.ino new file mode 100644 index 000000000..e11a0875e --- /dev/null +++ b/examples/ReceiveOneAndSendMultiple/ReceiveOneAndSendMultiple.ino @@ -0,0 +1,259 @@ +/* + * ReceiveOneAndSendMultiple.cpp + * + * Serves as a IR remote macro expander + * Receives Samsung32 protocol and on receiving a specified input frame, + * it sends multiple Samsung32 frames with appropriate delays in between. + * This serves as a Netflix-key emulation for my old Samsung H5273 TV. + * + * Tested on a digispark ATTiny85 board using AttinyCore https://github.com/SpenceKonde/ATTinyCore + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +// Digispark ATMEL ATTINY85 +// Piezo speaker must have a 270 ohm resistor in series for USB programming and running at the Samsung TV. +// IR LED has a 270 ohm resistor in series. +// +-\/-+ +// !RESET (5) PB5 1| |8 Vcc +// USB+ 3.6V Z-Diode, 1.5kOhm to VCC Piezo (3) PB3 2| |7 PB2 (2) TX Debug output +// USB- 3.6V Z-Diode IR Output (4) PB4 3| |6 PB1 (1) Feedback LED +// GND 4| |5 PB0 (0) IR Input +// +----+ +/* SAUMSUMG REMOTE CODES (Model: BN59-01180A) - Address is 0x07 + * Power Button - 0x2 + * Power Off - 0x98 + * 1 - 0x4 + * 2 - 0x5 + * 3 - 0x6 + * 4 - 0x8 + * 5 - 0x9 + * 6 - 0xa + * 7 - 0xc + * 8 - 0xd + * 9 - 0xe + * CH List - 0x6b + * Vol + - 0x7 + * Vol - - 0xb + * Mute - 0xf + * Source - 0x1 + * Ch + - 0x12 + * Ch - - 0x10 + * Menu - 0x1a + * Home - 0x79 + * MagicInfo Player - 0x30 + * Tools - 0x4b + * Info - 0x1f + * Up arrow - 0x60 + * Left arrow - 0x65 + * Right arrow - 0x62 + * Down arrow - 0x61 + * Return - 0x58 + * Exit - 0x2d + * A - 0x6c + * B - 0x14 + * C - 0x15 + * D - 0x16 + * Set - 0xab + * Unset - 0xac + * Lock - 0x77 + * Stop (square) - 0x46 + * Rewind (arrows) - 0x45 + * Play (triangle) - 0x47 + * Pause (bars) - 0x4a + * Fast Forward (arrows) - 0x48 + */ + +#include + +// select only Samsung protocol for sending and receiving +#define DECODE_SAMSUNG +#define ADDRESS_OF_SAMSUNG_REMOTE 0x07 // The value you see as address in printIRResultShort() + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. +#include + +void sendSamsungSmartHubMacro(bool aDoSelect); +void IRSendWithDelay(uint8_t aCommand, uint16_t aDelayMillis); + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // tone before IR setup, since it kills the IR timer settings + tone(TONE_PIN, 2200, 400); + digitalWrite(LED_BUILTIN, HIGH); + delay(400); + digitalWrite(LED_BUILTIN, LOW); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); + + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin + Serial.println(F("Ready to send IR signals at pin " STR(IR_SEND_PIN))); +} + +void loop() { + /* + * Check if new data available and get them + */ + if (IrReceiver.decode()) { + // Print a short summary of received data + IrReceiver.printIRResultShort(&Serial); + IrReceiver.printIRSendUsage(&Serial); + Serial.println(); + + /* + * Here data is available -> evaluate IR command + */ + switch (IrReceiver.decodedIRData.command) { + case 0x47: // The play key on the bottom of my Samsung remote + Serial.println(F("Play key detected, open Netflix")); + sendSamsungSmartHubMacro(true); + break; + + case 0x4A: // The pause key on the bottom of my Samsung remote + Serial.println(F("Pause key detected, open SmartHub")); + sendSamsungSmartHubMacro(false); + break; + + default: + break; + } + + /* + * !!!Important!!! Enable receiving of the next value, + * since receiving has stopped after the end of the current received data packet. + */ + IrReceiver.restartAfterSend(); // Is a NOP if sending does not require a timer. + IrReceiver.resume(); // Enable receiving of the next value + } +} + +void IRSendWithDelay(uint8_t aCommand, uint16_t aDelayMillis) { + IrSender.sendSamsung(ADDRESS_OF_SAMSUNG_REMOTE, aCommand, 1); // send with one repeat + Serial.print(F("Send Samsung command 0x")); + Serial.println(aCommand); + delay(aDelayMillis); +} + +bool sMacroWasCalledBefore = false; +#define INITIAL_WAIT_TIME_APPS_READY_MILLIS 70000 // Time to let the TV load all software before Netflix can be started without an error +#define INITIAL_WAIT_TIME_SMARTHUB_READY_MILLIS 20000 // Time to let the TV load all software before SmartHub manu can be displayed + +/* + * This macro calls the last SmartHub application you selected manually + * + * @param aDoSelect - if true select the current app (needs longer initial wait time) else show smarthub menu + * + */ +void sendSamsungSmartHubMacro(bool aDoSelect) { + uint32_t tWaitTimeAfterBoot; + if (aDoSelect) { + tWaitTimeAfterBoot = INITIAL_WAIT_TIME_APPS_READY_MILLIS; + } else { + tWaitTimeAfterBoot = INITIAL_WAIT_TIME_SMARTHUB_READY_MILLIS; + } + +#if !defined(ESP32) // ESP32 uses another timer for tone(), so the receiver must not be stopped and restarted for it + IrReceiver.stopTimer(); +#endif + if (millis() < tWaitTimeAfterBoot) { + // division by 1000 and printing requires much (8%) program memory + Serial.print(F("It is ")); + Serial.print(millis() / 1000); + Serial.print(F(" seconds after boot, Samsung H5273 TV requires ")); + Serial.print(tWaitTimeAfterBoot / 1000); + Serial.println(F(" seconds after boot to be ready for the command")); + + tone(TONE_PIN, 2200, 100); + delay(200); + tone(TONE_PIN, 2200, 100); + delay(100); + + if (millis() < tWaitTimeAfterBoot) { + Serial.print(F("Now do a blocking wait for ")); + Serial.print(tWaitTimeAfterBoot - millis()); + Serial.println(F(" milliseconds")); + delay(tWaitTimeAfterBoot - millis()); + } + } + + // Do beep feedback for special key to be received + tone(TONE_PIN, 2200, 200); + delay(200); + +#if !defined(ESP32) + IrReceiver.restartTimer(); // Restart IR timer. +#endif + + Serial.println(F("Wait for \"not supported\" to disappear")); + delay(2000); + + Serial.println(F("Start sending of Samsung IR macro")); + + IRSendWithDelay(0x1A, 2000); // Menu and wait for the Menu to pop up + + Serial.println(F("Wait for the menu to pop up")); + if (!sMacroWasCalledBefore) { + delay(2000); // wait additional time for the Menu load + } + + for (uint_fast8_t i = 0; i < 4; ++i) { + IRSendWithDelay(0x61, 250); // Down arrow. For my Samsung, the high byte of the command is the inverse of the low byte + } + + IRSendWithDelay(0x62, 400); // Right arrow + for (uint_fast8_t i = 0; i < 2; ++i) { + IRSendWithDelay(0x61, 250); // Down arrow + } + + delay(250); + IRSendWithDelay(0x68, 1); // Enter for SmartHub + + if (aDoSelect) { + Serial.println(F("Wait for SmartHub to show up, before entering current application")); + delay(10000); // Wait not longer than 12 seconds, because smarthub menu then disappears + IRSendWithDelay(0x68, 1); // Enter for last application (e.g. Netflix or Amazon) + } + + sMacroWasCalledBefore = true; + Serial.println(F("Done")); + +} diff --git a/examples/ReceiverTimingAnalysis/ReceiverTimingAnalysis.ino b/examples/ReceiverTimingAnalysis/ReceiverTimingAnalysis.ino new file mode 100644 index 000000000..ab732bcd0 --- /dev/null +++ b/examples/ReceiverTimingAnalysis/ReceiverTimingAnalysis.ino @@ -0,0 +1,253 @@ +/* + * ReceiverTimingAnalysis.cpp + * + * This program enables the pin change interrupt at pin 3 and waits for NEC (or other Pulse-Distance-Coding) IR Signal. + * It measures the pulse and pause times of the incoming signal and computes some statistics for it. + * + * Observed values: + * Delta of each signal type is around 50 up to 100 and at low signals up to 200. TSOP is better, especially at low IR signal level. + * VS1838 Mark Excess -50 to +50 us + * TSOP31238 Mark Excess 0 to +50 + * + * + * Copyright (C) 2019-2020 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRMP https://github.com/IRMP-org/IRMP. + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * IRMP is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include + +#define IR_RECEIVE_PIN 2 +//#define IR_RECEIVE_PIN 3 + +/* + * Helper macro for getting a macro definition as string + */ +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +#if !(defined(EICRA) && defined(EIFR) && defined(EIMSK)) +void measureTimingISR(void); +#endif + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + // Wait until Serial Monitor is attached. + // Required for boards using USB code for Serial like Leonardo. + // Is void for USB Serial implementations using external chips e.g. a CH340. + while (!Serial) + ; + // !!! Program will not proceed if no Serial Monitor is attached !!! +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__)); + +#if defined(EICRA) && defined(EIFR) && defined(EIMSK) +# if (IR_RECEIVE_PIN == 2) + EICRA |= _BV(ISC00); // interrupt on any logical change + EIFR |= _BV(INTF0); // clear interrupt bit + EIMSK |= _BV(INT0); // enable interrupt on next change +# elif (IR_RECEIVE_PIN == 3) + EICRA |= _BV(ISC10); // enable interrupt on pin3 on both edges for ATmega328 + EIFR |= _BV(INTF1); // clear interrupt bit + EIMSK |= _BV(INT1); // enable interrupt on next change +# endif +#else +# if defined(ARDUINO_ARCH_SAMD) // see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ paragraph: Syntax + attachInterrupt(IR_RECEIVE_PIN, measureTimingISR, CHANGE); +# else + attachInterrupt(digitalPinToInterrupt(IR_RECEIVE_PIN), measureTimingISR, CHANGE); +# endif +#endif + Serial.println(F("Ready to analyze NEC IR signal at pin " STR(IR_RECEIVE_PIN))); + Serial.println(); +} + +uint8_t ISREdgeCounter = 0; +volatile uint32_t LastMicros; +struct timingStruct { + uint16_t minimum; + uint8_t indexOfMinimum; + uint16_t maximum; + uint8_t indexOfMaximum; + uint16_t average; + + uint16_t SumForAverage; + uint8_t SampleCount; +// uint8_t LastPrintedCount; +}; + +struct timingStruct Mark; +struct timingStruct ShortSpace; +struct timingStruct LongSpace; + +/* + * Compute minimum, maximum and average + */ +void processTmingValue(struct timingStruct *aTimingStruct, uint16_t aValue) { + if (aTimingStruct->SampleCount == 0) { + // initialize values + aTimingStruct->minimum = UINT16_MAX; + aTimingStruct->maximum = 0; + aTimingStruct->SumForAverage = 0; + } + + if (aTimingStruct->minimum > aValue) { + aTimingStruct->minimum = aValue; + aTimingStruct->indexOfMinimum = aTimingStruct->SampleCount; + } + if (aTimingStruct->maximum < aValue) { + aTimingStruct->maximum = aValue; + aTimingStruct->indexOfMaximum = aTimingStruct->SampleCount; + } + + aTimingStruct->SampleCount++; + aTimingStruct->SumForAverage += aValue; + aTimingStruct->average = (aTimingStruct->SumForAverage + (aTimingStruct->SampleCount / 2)) / aTimingStruct->SampleCount; + +} + +void printTimingValues(struct timingStruct *aTimingStruct, const char *aCaption) { +// if (aTimingStruct->LastPrintedCount != aTimingStruct->SampleCount) +// { +// aTimingStruct->LastPrintedCount = aTimingStruct->SampleCount; + Serial.print(aCaption); + Serial.print(F(": SampleCount=")); + Serial.print(aTimingStruct->SampleCount); + Serial.print(F(" Minimum=")); + Serial.print(aTimingStruct->minimum); + Serial.print(F(" @")); + Serial.print(aTimingStruct->indexOfMinimum); + Serial.print(F(" Maximum=")); + Serial.print(aTimingStruct->maximum); + Serial.print(F(" @")); + Serial.print(aTimingStruct->indexOfMaximum); + Serial.print(F(" Delta=")); + Serial.print(aTimingStruct->maximum - aTimingStruct->minimum); + Serial.print(F(" Average=")); + Serial.print(aTimingStruct->average); + + Serial.println(); +// } +} + +void loop() { + if (Mark.SampleCount >= 32) { + /* + * This check enables statistics for longer protocols like Kaseikyo/Panasonics + */ +#if !defined(ARDUINO_ARCH_MBED) + noInterrupts(); +#endif + uint32_t tLastMicros = LastMicros; +#if !defined(ARDUINO_ARCH_MBED) + interrupts(); +#endif + uint32_t tMicrosDelta = micros() - tLastMicros; + + if (tMicrosDelta > 10000) { + // NEC signal ended just now + Serial.println(); + printTimingValues(&Mark, "Mark "); + printTimingValues(&ShortSpace, "ShortSpace"); + printTimingValues(&LongSpace, "LongSpace "); + + /* + * Print analysis of mark and short spaces + */ + Serial.println(F("Analysis :")); + Serial.print(F(" (Average of mark + short space)/2 = ")); + int16_t MarkAndShortSpaceAverage = (Mark.average + ShortSpace.average) / 2; + Serial.print(MarkAndShortSpaceAverage); + Serial.print(F(" us\r\n Delta (to NEC standard 560) = ")); + Serial.print(MarkAndShortSpaceAverage - 560); + Serial.print(F("us\r\n MARK_EXCESS_MICROS = (Average of mark - Average of mark and short space) = ")); + Serial.print((int16_t) Mark.average - MarkAndShortSpaceAverage); + Serial.print(F("us")); + Serial.println(); + Serial.println(); + + Mark.SampleCount = 0; // used as flag for not printing the results more than once + } + } +} + +/* + * The interrupt handler. + * Just add to the appropriate timing structure. + */ +#if defined(ESP8266) || defined(ESP32) +void IRAM_ATTR measureTimingISR() +#else +# if defined(EICRA) && defined(EIFR) && defined(EIMSK) +# if (IR_RECEIVE_PIN == 2) +ISR(INT0_vect) +# elif (IR_RECEIVE_PIN == 3) +ISR(INT1_vect) +# endif +# else +void measureTimingISR() +# endif +#endif +{ + uint32_t tMicros = micros(); + uint32_t tMicrosDelta = tMicros - LastMicros; + LastMicros = tMicros; + /* + * read level and give feedback + */ + uint8_t tInputLevel = digitalRead(IR_RECEIVE_PIN); + digitalWrite(LED_BUILTIN, !tInputLevel); + + if (tMicrosDelta > 10000) { + // gap > 10 ms detected, reset counter to first detected edge and initialize timing structures + ISREdgeCounter = 1; + LongSpace.SampleCount = 0; + ShortSpace.SampleCount = 0; + Mark.SampleCount = 0; + } else { + ISREdgeCounter++; + } + + /* + * Skip header mark and space and first bit mark and space + */ + if (ISREdgeCounter > 4) { + if (tInputLevel != LOW) { + // Mark ended + processTmingValue(&Mark, tMicrosDelta); +// Serial.print('M'); + } else { + // Space ended + if (tMicrosDelta > 1000) { + // long space - logical 1 + processTmingValue(&LongSpace, tMicrosDelta); + Serial.print('1'); + } else { + // short space - logical 0 + processTmingValue(&ShortSpace, tMicrosDelta); + Serial.print('0'); + } + } + } +} diff --git a/examples/SendAndReceive/PinDefinitionsAndMore.h b/examples/SendAndReceive/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/SendAndReceive/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/SendAndReceive/SendAndReceive.ino b/examples/SendAndReceive/SendAndReceive.ino new file mode 100644 index 000000000..9b80ab7ed --- /dev/null +++ b/examples/SendAndReceive/SendAndReceive.ino @@ -0,0 +1,163 @@ +/* + * SendAndReceive.cpp + * + * Demonstrates sending IR codes and receiving it simultaneously + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2021-2023 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +#include + +// select only NEC and the universal decoder for pulse distance protocols +#define DECODE_NEC // Includes Apple and Onkyo +#define DECODE_DISTANCE_WIDTH // In case NEC is not received correctly. Universal decoder for pulse distance width protocols + +//#define EXCLUDE_UNIVERSAL_PROTOCOLS // Saves up to 1000 bytes program memory. +//#define EXCLUDE_EXOTIC_PROTOCOLS // saves around 650 bytes program memory if all other protocols are active +//#define NO_LED_FEEDBACK_CODE // saves 92 bytes program memory +//#define RECORD_GAP_MICROS 12000 // Default is 8000. Activate it for some LG air conditioner protocols +//#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. +//#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition + +//#define DEBUG // Activate this for lots of lovely debug output from the decoders. + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. +#include + +#define DELAY_AFTER_SEND 2000 +#define DELAY_AFTER_LOOP 5000 + +void setup() { + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin + Serial.println(F("Send IR signals at pin " STR(IR_SEND_PIN))); + +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604 +// For esp32 we use PWM generation by ledcWrite() for each pin. +# if !defined(SEND_PWM_BY_TIMER) && !defined(USE_NO_SEND_PWM) && !defined(ESP32) + /* + * Print internal software PWM generation info + */ + IrSender.enableIROut(38); // Call it with 38 kHz to initialize the values printed below + Serial.print(F("Send signal mark duration is ")); + Serial.print(IrSender.periodOnTimeMicros); + Serial.print(F(" us, pulse correction is ")); + Serial.print(IrSender.getPulseCorrectionNanos()); + Serial.print(F(" ns, total period is ")); + Serial.print(IrSender.periodTimeMicros); + Serial.println(F(" us")); +# endif + + // infos for receive + Serial.print(RECORD_GAP_MICROS); + Serial.println(F(" us is the (minimum) gap, after which the start of a new IR packet is assumed")); + Serial.print(MARK_EXCESS_MICROS); + Serial.println(F(" us are subtracted from all marks and added to all spaces for decoding")); +#endif +} + +uint16_t sAddress = 0x0102; +uint8_t sCommand = 0x34; +uint8_t sRepeats = 1; + +/* + * Send NEC IR protocol + */ +void send_ir_data() { + Serial.print(F("Sending: 0x")); + Serial.print(sAddress, HEX); + Serial.print(sCommand, HEX); + Serial.println(sRepeats, HEX); + Serial.flush(); // To avoid disturbing the software PWM generation by serial output interrupts + + // clip repeats at 4 + if (sRepeats > 4) { + sRepeats = 4; + } + // Results for the first loop to: Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 (32 bits) + IrSender.sendNEC(sAddress, sCommand, sRepeats); +} + +void receive_ir_data() { + if (IrReceiver.decode()) { + Serial.print(F("Decoded protocol: ")); + Serial.print(getProtocolString(IrReceiver.decodedIRData.protocol)); + Serial.print(F(", decoded raw data: ")); +#if (__INT_WIDTH__ < 32) + Serial.print(IrReceiver.decodedIRData.decodedRawData, HEX); +#else + PrintULL::print(&Serial, IrReceiver.decodedIRData.decodedRawData, HEX); +#endif + Serial.print(F(", decoded address: ")); + Serial.print(IrReceiver.decodedIRData.address, HEX); + Serial.print(F(", decoded command: ")); + Serial.println(IrReceiver.decodedIRData.command, HEX); + IrReceiver.resume(); + } +} + +void loop() { + /* + * Print loop values + */ + Serial.println(); + Serial.print(F("address=0x")); + Serial.print(sAddress, HEX); + Serial.print(F(" command=0x")); + Serial.print(sCommand, HEX); + Serial.print(F(" repeats=")); + Serial.println(sRepeats); + Serial.flush(); + + send_ir_data(); + IrReceiver.restartAfterSend(); // Is a NOP if sending does not require a timer. + + // wait for the receiver state machine to detect the end of a protocol + delay((RECORD_GAP_MICROS / 1000) + 5); + receive_ir_data(); + + // Prepare data for next loop + sAddress += 0x0101; + sCommand += 0x11; + sRepeats++; + + delay(100); // Loop delay +} diff --git a/examples/SendAndReceive/SendAndReceive.log b/examples/SendAndReceive/SendAndReceive.log new file mode 100644 index 000000000..ec6dc73b7 --- /dev/null +++ b/examples/SendAndReceive/SendAndReceive.log @@ -0,0 +1,39 @@ +START ../src/SendAndReceive.cpp from Feb 24 2023 +Using library version 4.1.0 +Ready to receive IR signals of protocols: NEC/NEC2/Onkyo/Apple, Universal Pulse Distance Width, at pin 2 +Send IR signals at pin 3 +Send signal mark duration is 8 us, pulse correction is 3000 ns, total period is 26 us +5000 us is the (minimum) gap, after which the start of a new IR packet is assumed +20 us are subtracted from all marks and added to all spaces for decoding + +address=0x102 command=0x34 repeats=1 +Sending: 0x102341 +Decoded protocol: NEC2, decoded raw data: CB340102, decoded address: 102, decoded command: 34 + +address=0x203 command=0x45 repeats=2 +Sending: 0x203452 +Decoded protocol: NEC, decoded raw data: BA450203, decoded address: 203, decoded command: 45 + +address=0x304 command=0x56 repeats=3 +Sending: 0x304563 +Decoded protocol: NEC, decoded raw data: A9560304, decoded address: 304, decoded command: 56 + +address=0x405 command=0x67 repeats=4 +Sending: 0x405674 +Decoded protocol: NEC, decoded raw data: 98670405, decoded address: 405, decoded command: 67 + +address=0x506 command=0x78 repeats=5 +Sending: 0x506785 +Decoded protocol: NEC, decoded raw data: 87780506, decoded address: 506, decoded command: 78 + +address=0x607 command=0x89 repeats=5 +Sending: 0x607895 +Decoded protocol: NEC, decoded raw data: 76890607, decoded address: 607, decoded command: 89 + +address=0x708 command=0x9A repeats=5 +Sending: 0x7089A5 +Decoded protocol: NEC, decoded raw data: 659A0708, decoded address: 708, decoded command: 9A + +address=0x809 command=0xAB repeats=5 +Sending: 0x809AB5 +Decoded protocol: NEC, decoded raw data: 54AB0809, decoded address: 809, decoded command: AB diff --git a/examples/SendBoseWaveDemo/PinDefinitionsAndMore.h b/examples/SendBoseWaveDemo/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/SendBoseWaveDemo/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/SendBoseWaveDemo/SendBoseWaveDemo.ino b/examples/SendBoseWaveDemo/SendBoseWaveDemo.ino new file mode 100644 index 000000000..5046b96cf --- /dev/null +++ b/examples/SendBoseWaveDemo/SendBoseWaveDemo.ino @@ -0,0 +1,210 @@ +/* + * SendBoseWaveDemo.cpp + * + * Prompt user for a code to send. Make sure your 940-950nm IR LED is + * connected to the default digital output. Place your Bose Wave Radio + * CD in the line of sight of your LED, and send commands! + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020 Thomas Koch - 2022 AJ converted to inverted bits + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +#if !defined(ARDUINO_ESP32C3_DEV) // This is due to a bug in RISC-V compiler, which requires unused function sections :-(. +#define DISABLE_CODE_FOR_RECEIVER // Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. +#endif + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. +#include + +//...................................................................... +// +// Bose Wave Radio CD Remote Control +// |-------------------------------------| +// | On/Off Sleep VolUp | +// | Play/Pause Stop VolDown | +// | FM AM Aux | +// | Tune Down Tune Up Mute | +// | 1 2 3 | +// | 4 5 6 | +// |-------------------------------------| +#define BOSE_CMD_ON_OFF 0x00 +#define BOSE_CMD_MUTE 0x01 +#define BOSE_CMD_VOL_UP 0x02 +#define BOSE_CMD_VOL_DOWN 0x03 +#define BOSE_CMD_PRESET_6 0x04 +#define BOSE_CMD_SLEEP 0x05 +#define BOSE_CMD_FM 0x06 +#define BOSE_CMD_AUX 0x07 +#define BOSE_CMD_AM 0x08 +#define BOSE_CMD_PLAY_PAUSE 0x09 +#define BOSE_CMD_STOP 0x0A +#define BOSE_CMD_TUNE_UP 0x0B +#define BOSE_CMD_TUNE_DOWN 0x0C +#define BOSE_CMD_PRESET_1 0x0D +#define BOSE_CMD_PRESET_2 0x0E +#define BOSE_CMD_PRESET_3 0x0F +#define BOSE_CMD_PRESET_4 0x10 +#define BOSE_CMD_PRESET_5 0x11 + +// Codes for Wave Music System +// https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/BoseWaveMusicSystem.jpg) +//#define BOSE_CMD_ON_OFF 0x4C +//#define BOSE_CMD_MUTE 0x01 +//#define BOSE_CMD_VOL_UP 0x03 +//#define BOSE_CMD_VOL_DOWN 0x02 +//#define BOSE_CMD_SLEEP 0x54 +//#define BOSE_CMD_FM_AM 0x06 +//#define BOSE_CMD_CD 0x53 +//#define BOSE_CMD_AUX 0x0F +//#define BOSE_CMD_TRACK_BW 0x18 +//#define BOSE_CMD_TRACK_FW 0x19 +//#define BOSE_CMD_PLAY_PAUSE 0x1B +//#define BOSE_CMD_STOP_EJECT 0x1A +//#define BOSE_CMD_TUNE_UP 0x58 +//#define BOSE_CMD_TUNE_DOWN 0x57 +//#define BOSE_CMD_PRESET_1 0x07 +//#define BOSE_CMD_PRESET_2 0x08 +//#define BOSE_CMD_PRESET_3 0x09 +//#define BOSE_CMD_PRESET_4 0x0A +//#define BOSE_CMD_PRESET_5 0x0B +//#define BOSE_CMD_PRESET_6 0x0C +//#define BOSE_CMD_TIME_MINUS 0x9E +//#define BOSE_CMD_TIME_PLUS 0x24 +//#define BOSE_CMD_PLAY_MODE 0x21 +//#define BOSE_CMD_ALARM_ON_OFF 0x22 +//#define BOSE_CMD_ALARM_WAKE_TO 0x70 +//#define BOSE_CMD_ALARM_TIME 0x23 +// Different last 3 codes for Wave Sound Touch IV +//#define BOSE_CMD_ALARM_1 0x22 +//#define BOSE_CMD_ALARM_2 0x62 +//#define BOSE_CMD_ALARM_SETUP 0xA2 + +bool sPrintMenu; +void printMenu(); + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + +#if defined(IR_SEND_PIN) + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin + Serial.println(F("Send IR signals at pin " STR(IR_SEND_PIN))); +#else + uint8_t tSendPin = 3; + IrSender.begin(tSendPin, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN); // Specify send pin and enable feedback LED at default feedback LED pin + // You can change send pin later with IrSender.setSendPin(); + + Serial.print(F("Send IR signals at pin ")); + Serial.println(tSendPin); +#endif + + sPrintMenu = true; +} + +void loop() { + if (sPrintMenu) { + sPrintMenu = false; + printMenu(); + } + int tSerialCommandCharacter; + + if (Serial.available()) { + tSerialCommandCharacter = Serial.read(); + sPrintMenu = true; + if (tSerialCommandCharacter == -1) { + Serial.print(F("available() was true, but no character read")); // should not happen + } else if (tSerialCommandCharacter == 48) { // 0 + IrSender.sendBoseWave(BOSE_CMD_ON_OFF); // On/Off + } else if (tSerialCommandCharacter == 49) { // 1 + IrSender.sendBoseWave(BOSE_CMD_VOL_UP); // Volume Up + } else if (tSerialCommandCharacter == 50) { // 2 + IrSender.sendBoseWave(BOSE_CMD_VOL_DOWN); // Volume Down + } else if (tSerialCommandCharacter == 51) { // 3 + IrSender.sendBoseWave(BOSE_CMD_TUNE_UP); // Tune Up + } else if (tSerialCommandCharacter == 52) { // 4 + IrSender.sendBoseWave(BOSE_CMD_TUNE_DOWN); // Tune Down + } else if (tSerialCommandCharacter == 53) { // 5 + IrSender.sendBoseWave(BOSE_CMD_AM); // AM + } else if (tSerialCommandCharacter == 54) { // 6 + IrSender.sendBoseWave(BOSE_CMD_FM); // FM + } else if (tSerialCommandCharacter == 55) { // 7 + IrSender.sendBoseWave(BOSE_CMD_PRESET_1); // Preset 1 + } else if (tSerialCommandCharacter == 56) { // 8 + IrSender.sendBoseWave(BOSE_CMD_PRESET_2); // Preset 2 + } else if (tSerialCommandCharacter == 57) { // 9 + IrSender.sendBoseWave(BOSE_CMD_PRESET_3); // Preset 3 + } else if (tSerialCommandCharacter == 97) { // a + IrSender.sendBoseWave(BOSE_CMD_PRESET_4); // Preset 4 + } else if (tSerialCommandCharacter == 98) { // b + IrSender.sendBoseWave(BOSE_CMD_PRESET_5); // Preset 5 + } else if (tSerialCommandCharacter == 99) { // c + IrSender.sendBoseWave(BOSE_CMD_PRESET_6); // Preset 6 + } else if (tSerialCommandCharacter == 100) { // d + IrSender.sendBoseWave(BOSE_CMD_MUTE); // Mute + } else if (tSerialCommandCharacter == 101) { // e + IrSender.sendBoseWave(BOSE_CMD_PLAY_PAUSE); // Pause + } else if (tSerialCommandCharacter == 102) { // f + IrSender.sendBoseWave(BOSE_CMD_STOP); // Stop + } else if (tSerialCommandCharacter == 103) { // g + IrSender.sendBoseWave(BOSE_CMD_AUX); // Aux + } else if (tSerialCommandCharacter == 104) { // h + IrSender.sendBoseWave(BOSE_CMD_SLEEP); // Sleep + } else { + sPrintMenu = false; + } + delay(300); + } +} + +void printMenu() { + Serial.println("0: On / Off"); + Serial.println("1: Volume Up"); + Serial.println("2: Volume Down"); + Serial.println("3: Tune Up"); + Serial.println("4: Tune Down"); + Serial.println("5: AM"); + Serial.println("6: FM"); + Serial.println("7: Preset 1"); + Serial.println("8: Preset 2"); + Serial.println("9: Preset 3"); + Serial.println("a: Preset 4"); + Serial.println("b: Preset 5"); + Serial.println("c: Preset 6"); + Serial.println("d: Mute"); + Serial.println("e: Play/Pause"); + Serial.println("f: Stop"); + Serial.println("g: Aux"); + Serial.println("h: Sleep"); +} diff --git a/examples/SendDemo/IRremote_SendDemo_ReceiveDemo.log b/examples/SendDemo/IRremote_SendDemo_ReceiveDemo.log new file mode 100644 index 000000000..d56a9481f --- /dev/null +++ b/examples/SendDemo/IRremote_SendDemo_ReceiveDemo.log @@ -0,0 +1,178 @@ +START ../src/ReceiveDemo.cpp from Feb 24 2023 +Using library version 4.1.0 +Enabling IRin... +Ready to receive IR signals of protocols: NEC/NEC2/Onkyo/Apple, Panasonic/Kaseikyo, Denon/Sharp, Sony, RC5, RC6, LG, JVC, Samsung, FAST, Whynter, Lego Power Functions, Bosewave , MagiQuest, Universal Pulse Distance Width, Hash at pin 2 + +If you connect debug pin 5 to ground, raw data is always printed +5000 us is the (minimum) gap, after which the start of a new IR packet is assumed +20 us are subtracted from all marks and added to all spaces for decoding + +Protocol=NEC Address=0x2 Command=0x34 Raw-Data=0xCB34FD02 32 bits LSB first +Send with: IrSender.sendNEC(0x2, 0x34, ); + +Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendNEC(0x102, 0x34, ); + +Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendNEC(0x102, 0x34, ); + +Protocol=NEC Address=0x80 Command=0x45 Raw-Data=0xBA457F80 32 bits LSB first +Send with: IrSender.sendNEC(0x80, 0x45, ); + +Protocol=NEC Address=0x4 Command=0x8 Raw-Data=0xF708FB04 32 bits LSB first +Send with: IrSender.sendNEC(0x4, 0x8, ); + +Protocol=Onkyo Address=0x102 Command=0x304 Raw-Data=0x3040102 32 bits LSB first +Send with: IrSender.sendOnkyo(0x102, 0x304, ); + +Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendNEC(0x102, 0x34, ); + +Protocol=Panasonic Address=0xB Command=0x10 Raw-Data=0xA01000B0 48 bits LSB first +Send with: IrSender.sendPanasonic(0xB, 0x10, ); + +Protocol=Panasonic Address=0xB Command=0x10 Raw-Data=0xA01000B0 48 bits LSB first +Send with: IrSender.sendPanasonic(0xB, 0x10, ); + +Protocol=Panasonic Address=0xB Command=0x10 Raw-Data=0xA01000B0 48 bits LSB first +Send with: IrSender.sendPanasonic(0xB, 0x10, ); + +Protocol=PulseDistance Raw-Data=0x5A 72 bits LSB first +Send with: + uint32_t tRawData[]={0x87654321, 0xAFEDCBA9, 0x5A}; + IrSender.sendPulseDistanceWidthFromArray(38, 8850, 4400, 550, 1700, 550, 600, &tRawData[0], 72, PROTOCOL_IS_LSB_FIRST, , ); + +Protocol=PulseWidth Raw-Data=0xDCBA9 52 bits LSB first +Send with: + uint32_t tRawData[]={0x87654321, 0xDCBA9}; + IrSender.sendPulseDistanceWidthFromArray(38, 300, 600, 600, 300, 350, 600, &tRawData[0], 52, PROTOCOL_IS_LSB_FIRST, , ); + +Protocol=PulseWidth Raw-Data=0x87654321 32 bits LSB first +Send with: IrSender.sendPulseDistanceWidth(38, 1000, 500, 600, 300, 350, 300, 0x87654321, 32, PROTOCOL_IS_LSB_FIRST, , ); + +Protocol=Onkyo Address=0x102 Command=0x5634 Raw-Data=0x56340102 32 bits LSB first +Send with: IrSender.sendOnkyo(0x102, 0x5634, ); + +Protocol=Apple Address=0x2 Command=0x34 Raw-Data=0x23487EE 32 bits LSB first +Send with: IrSender.sendApple(0x2, 0x34, ); + +Protocol=Panasonic Address=0x102 Command=0x34 Raw-Data=0x4341020 48 bits LSB first +Send with: IrSender.sendPanasonic(0x102, 0x34, ); + +Protocol=Kaseikyo Address=0x102 Command=0x34 Extra=0x4711 Raw-Data=0x7341023 48 bits LSB first +Send with: IrSender.sendKaseikyo(0x102, 0x34, , 0x4711); + +Protocol=Kaseikyo_Denon Address=0x102 Command=0x34 Raw-Data=0x4341020 48 bits LSB first +Send with: IrSender.sendKaseikyo_Denon(0x102, 0x34, ); + +Protocol=Denon Address=0x2 Command=0x34 Raw-Data=0x682 15 bits LSB first +Send with: IrSender.sendDenon(0x2, 0x34, ); + +Protocol=Denon Address=0x2 Command=0x34 Auto-Repeat gap=45650us Raw-Data=0x7962 15 bits LSB first + +Protocol=Sharp Address=0x2 Command=0x34 Raw-Data=0x4682 15 bits LSB first +Send with: IrSender.sendSharp(0x2, 0x34, ); + +Protocol=Sharp Address=0x2 Command=0x34 Auto-Repeat gap=46400us Raw-Data=0x3962 15 bits LSB first + +Protocol=Sony Address=0x2 Command=0x34 Raw-Data=0x134 12 bits LSB first +Send with: IrSender.sendSony(0x2, 0x34, 2, 12); + +Protocol=Sony Address=0x2 Command=0x34 Raw-Data=0x134 15 bits LSB first +Send with: IrSender.sendSony(0x2, 0x34, 2, 15); + +Protocol=Sony Address=0x102 Command=0x34 Raw-Data=0x8134 20 bits LSB first +Send with: IrSender.sendSony(0x102, 0x34, 2, 20); + +Protocol=Samsung Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendSamsung(0x102, 0x34, ); + +Protocol=Samsung Address=0x102 Command=0x5634 Raw-Data=0x56340102 32 bits LSB first +Send with: IrSender.sendSamsung(0x102, 0x5634, ); + +Protocol=Samsung48 Address=0x102 Command=0x5634 Raw-Data=0xA956 48 bits LSB first +Send with: IrSender.sendSamsung48(0x102, 0x5634, ); + +Protocol=RC5 Address=0x2 Command=0x34 Raw-Data=0x10B4 13 bits MSB first +Send with: IrSender.sendRC5(0x2, 0x34, ); + +Protocol=RC5 Address=0x2 Command=0x74 Toggle=1 Raw-Data=0x8B4 13 bits MSB first +Send with: IrSender.sendRC5(0x2, 0x74, ); + +Protocol=RC6 Address=0x2 Command=0x34 Raw-Data=0x234 20 bits MSB first +Send with: IrSender.sendRC6(0x2, 0x34, ); + +Protocol=Samsung Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendSamsung(0x102, 0x34, ); + +Protocol=JVC Address=0x2 Command=0x34 Raw-Data=0x3402 16 bits LSB first +Send with: IrSender.sendJVC(0x2, 0x34, ); + +Protocol=Samsung Address=0x102 Command=0x5634 Raw-Data=0x56340102 32 bits LSB first +Send with: IrSender.sendSamsung(0x102, 0x5634, ); + +Protocol=LG Address=0x2 Command=0x5634 Raw-Data=0x256342 28 bits MSB first +Send with: IrSender.sendLG(0x2, 0x5634, ); + +Protocol=MagiQuest Address=0x102 Command=0x34 Raw-Data=0x6BCD0102 56 bits MSB first +Send with: IrSender.sendMagiQuest(0x6BCD0102, 0x34, ); + +Protocol=BoseWave Address=0x0 Command=0x34 Raw-Data=0xCB34 16 bits LSB first +Send with: IrSender.sendBoseWave(0x0, 0x34, ); + +Protocol=FAST Address=0x0 Command=0x34 Raw-Data=0xCB34 16 bits LSB first +Send with: IrSender.sendFAST(0x0, 0x34, ); + +Protocol=Lego Address=0x2 Command=0x14 Raw-Data=0x2148 16 bits MSB first +Send with: IrSender.sendLego(0x2, 0x14, ); + +Protocol=Lego Address=0x2 Command=0x14 Auto-Repeat gap=180450us Raw-Data=0x2148 16 bits MSB first + +Protocol=Lego Address=0x2 Command=0x14 Auto-Repeat gap=179350us Raw-Data=0x2148 16 bits MSB first + +Protocol=Lego Address=0x2 Command=0x14 Auto-Repeat gap=179200us Raw-Data=0x2148 16 bits MSB first + +Protocol=Lego Address=0x2 Command=0x14 Auto-Repeat gap=179150us Raw-Data=0x2148 16 bits MSB first + +Overflow detected +Try to increase the "RAW_BUFFER_LENGTH" value of 600 in ../src/ReceiveDemo.cpp + +Protocol=NEC Address=0x3 Command=0x45 Raw-Data=0xBA45FC03 32 bits LSB first +Send with: IrSender.sendNEC(0x3, 0x45, ); + +Protocol=NEC Address=0x3 Command=0x45 Repeat gap=43250us + +Protocol=NEC Address=0x203 Command=0x45 Raw-Data=0xBA450203 32 bits LSB first +Send with: IrSender.sendNEC(0x203, 0x45, ); + +Protocol=NEC Address=0x203 Command=0x45 Repeat gap=47550us + +Protocol=NEC Address=0x203 Command=0x45 Raw-Data=0xBA450203 32 bits LSB first +Send with: IrSender.sendNEC(0x203, 0x45, ); + +Protocol=NEC2 Address=0x203 Command=0x45 Repeat gap=46500us Raw-Data=0xBA450203 32 bits LSB first + +Protocol=Onkyo Address=0x203 Command=0x6745 Raw-Data=0x67450203 32 bits LSB first +Send with: IrSender.sendOnkyo(0x203, 0x6745, ); + +Protocol=Onkyo Address=0x203 Command=0x6745 Repeat gap=46550us + +Protocol=Apple Address=0x3 Command=0x45 Raw-Data=0x34587EE 32 bits LSB first +Send with: IrSender.sendApple(0x3, 0x45, ); + +Protocol=Apple Address=0x3 Command=0x45 Repeat gap=31550us + +Protocol=Panasonic Address=0x203 Command=0x45 Raw-Data=0x55452030 48 bits LSB first +Send with: IrSender.sendPanasonic(0x203, 0x45, ); + +Protocol=Panasonic Address=0x203 Command=0x45 Repeat gap=72550us Raw-Data=0x55452030 48 bits LSB first + +Protocol=Kaseikyo Address=0x203 Command=0x45 Extra=0x4711 Raw-Data=0x56452033 48 bits LSB first +Send with: IrSender.sendKaseikyo(0x203, 0x45, , 0x4711); + +Protocol=Kaseikyo Address=0x203 Command=0x45 Extra=0x4711 Repeat gap=66750us Raw-Data=0x56452033 48 bits LSB first + +Protocol=Kaseikyo_Denon Address=0x203 Command=0x45 Raw-Data=0x55452030 48 bits LSB first +Send with: IrSender.sendKaseikyo_Denon(0x203, 0x45, ); + +Protocol=Kaseikyo_Denon Address=0x203 Command=0x45 Repeat gap=68300us Raw-Data=0x55452030 48 bits LSB first \ No newline at end of file diff --git a/examples/SendDemo/PinDefinitionsAndMore.h b/examples/SendDemo/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/SendDemo/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/SendDemo/SendDemo.ino b/examples/SendDemo/SendDemo.ino new file mode 100644 index 000000000..c918aa3a1 --- /dev/null +++ b/examples/SendDemo/SendDemo.ino @@ -0,0 +1,470 @@ +/* + * SendDemo.cpp + * + * Demonstrates sending IR codes in standard format with address and command + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2025 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. + +#if !defined(ARDUINO_ESP32C3_DEV) // This is due to a bug in RISC-V compiler, which requires unused function sections :-(. +#define DISABLE_CODE_FOR_RECEIVER // Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. +#endif +//#define EXCLUDE_EXOTIC_PROTOCOLS // Saves around 240 bytes program memory if IrSender.write is used +//#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. +//#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition +//#define USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN // Simulate an active high receiver signal instead of an active low signal. +//#define USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN // Use or simulate open drain output mode at send pin. Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin! +#if FLASHEND <= 0x1FFF // For 8k flash or less like ATtiny85 +#define NO_LED_FEEDBACK_CODE // Saves 344 bytes program memory +#endif +//#undef IR_SEND_PIN // enable this, if you need to set send pin programmatically using uint8_t tSendPin below +#include + +#define DELAY_AFTER_SEND 2000 +#define DELAY_AFTER_LOOP 5000 + +#if __INT_WIDTH__ < 32 + IRRawDataType const tRawDataPGM[] PROGMEM = { 0xB02002, 0xA010 }; // LSB of tRawData[0] is sent first +#endif + +void setup() { + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + +#if defined(IR_SEND_PIN) + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin +// disableLEDFeedback(); // Disable feedback LED at default feedback LED pin +# if defined(IR_SEND_PIN_STRING) + Serial.println(F("Send IR signals at pin " IR_SEND_PIN_STRING)); +# else + Serial.println(F("Send IR signals at pin " STR(IR_SEND_PIN))); +# endif +#else + // Here the macro IR_SEND_PIN is not defined or undefined above with #undef IR_SEND_PIN + uint8_t tSendPin = 3; + IrSender.begin(tSendPin, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN); // Specify send pin and enable feedback LED at default feedback LED pin + // You can change send pin later with IrSender.setSendPin(); + + Serial.print(F("Send IR signals at pin ")); + Serial.println(tSendPin); +#endif + +#if !defined(SEND_PWM_BY_TIMER) + /* + * Print internal software PWM signal generation info + */ + IrSender.enableIROut(38); // Call it with 38 kHz just to initialize the values printed below + Serial.print(F("Send signal mark duration is ")); + Serial.print(IrSender.periodOnTimeMicros); + Serial.print(F(" us, pulse narrowing correction is ")); + Serial.print(IrSender.getPulseCorrectionNanos()); + Serial.print(F(" ns, total period is ")); + Serial.print(IrSender.periodTimeMicros); + Serial.println(F(" us")); +#endif + +#if defined(LED_BUILTIN) && !defined(NO_LED_FEEDBACK_CODE) +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + Serial.print(F("Active low ")); +# endif + Serial.print(F("FeedbackLED at pin ")); + Serial.println(LED_BUILTIN); // Works also for ESP32: static const uint8_t LED_BUILTIN = 8; #define LED_BUILTIN LED_BUILTIN +#endif + +} + +/* + * Set up the data to be sent. + * For most protocols, the data is build up with a constant 8 (or 16 byte) address + * and a variable 8 bit command. + * There are exceptions like Sony and Denon, which have 5 bit address. + */ +uint16_t sAddress = 0x0102; +uint8_t sCommand = 0x34; +uint16_t s16BitCommand = 0x5634; +uint8_t sRepeats = 0; + +void loop() { + /* + * Print values + */ + Serial.println(); + Serial.print(F("address=0x")); + Serial.print(sAddress, HEX); + Serial.print(F(" command=0x")); + Serial.print(sCommand, HEX); + Serial.print(F(" repeats=")); + Serial.println(sRepeats); + Serial.println(); + Serial.println(); + Serial.flush(); + + Serial.println(F("Send NEC with 8 bit address")); + Serial.flush(); + IrSender.sendNEC(sAddress & 0xFF, sCommand, sRepeats); + delay(DELAY_AFTER_SEND); // delay must be greater than 5 ms (RECORD_GAP_MICROS), otherwise the receiver sees it as one long signal + + Serial.println(F("Send NEC with 16 bit address")); + Serial.flush(); + IrSender.sendNEC(sAddress, sCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send NEC2 with 16 bit address")); + Serial.flush(); + IrSender.sendNEC2(sAddress, sCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + if (sRepeats == 0) { +#if FLASHEND >= 0x3FFF && ((!defined(RAMEND) && !defined(RAMSIZE)) || (defined(RAMEND) && RAMEND > 0x6FF) || (defined(RAMSIZE) && RAMSIZE > 0x6FF)) // For 16k flash or more, like ATtiny1604. Code does not fit in program memory of ATtiny85 etc. + /* + * Send constant values only once in this demo + */ + Serial.println(F("Sending NEC Pronto data with 8 bit address 0x80 and command 0x45 and no repeats")); + Serial.flush(); + IrSender.sendPronto(F("0000 006D 0022 0000 015E 00AB " /* Pronto header + start bit */ + "0017 0015 0017 0015 0017 0017 0015 0017 0017 0015 0017 0015 0017 0015 0017 003F " /* Lower address byte */ + "0017 003F 0017 003E 0017 003F 0015 003F 0017 003E 0017 003F 0017 003E 0017 0015 " /* Upper address byte (inverted at 8 bit mode) */ + "0017 003E 0017 0015 0017 003F 0017 0015 0017 0015 0017 0015 0017 003F 0017 0015 " /* command byte */ + "0019 0013 0019 003C 0017 0015 0017 003F 0017 003E 0017 003F 0017 0015 0017 003E " /* inverted command byte */ + "0017 0806"), 0); //stop bit, no repeat possible, because of missing repeat pattern + delay(DELAY_AFTER_SEND); + + /* + * !!! The next data occupies 136 bytes RAM !!! + */ + Serial.println( + F("Send NEC sendRaw data with 8 bit address=0xFB04 and command 0x08 and exact timing (16 bit array format)")); + Serial.flush(); + const uint16_t irSignal[] = { 9000, 4500/*Start bit*/, 560, 560, 560, 560, 560, 1690, 560, + 560/*0010 0x4 of 16 bit address LSB first*/, 560, 560, 560, 560, 560, 560, 560, 560/*0000*/, 560, 1690, 560, 1690, + 560, 560, 560, 1690/*1101 0xB*/, 560, 1690, 560, 1690, 560, 1690, 560, 1690/*1111*/, 560, 560, 560, 560, 560, 560, + 560, 1690/*0001 0x08 of command LSB first*/, 560, 560, 560, 560, 560, 560, 560, 560/*0000 0x00*/, 560, 1690, 560, + 1690, 560, 1690, 560, 560/*1110 Inverted 8 of command*/, 560, 1690, 560, 1690, 560, 1690, 560, + 1690/*1111 inverted 0 of command*/, 560 /*stop bit*/}; // Using exact NEC timing + IrSender.sendRaw(irSignal, sizeof(irSignal) / sizeof(irSignal[0]), NEC_KHZ); // Note the approach used to automatically calculate the size of the array. + delay(DELAY_AFTER_SEND); + + /* + * With sendNECRaw() you can send 32 bit combined codes + */ + Serial.println(F("Send ONKYO with 16 bit address 0x0102 and 16 bit command 0x0304 with NECRaw(0x03040102)")); + Serial.flush(); + IrSender.sendNECRaw(0x03040102, sRepeats); + delay(DELAY_AFTER_SEND); + + /* + * With Send sendNECMSB() you can send your old 32 bit codes. + * To convert one into the other, you must reverse the byte positions and then reverse all positions of each byte. + * Use bitreverse32Bit(). + * Example: + * 0xCB340102 byte reverse -> 0x020134CB bit reverse-> 40802CD3 + */ + Serial.println(F("Send ONKYO with 16 bit address 0x0102 and command 0x34 with old 32 bit format MSB first (0x40802CD3)")); + Serial.flush(); + IrSender.sendNECMSB(0x40802CD3, 32, false); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Panasonic 0xB, 0x10 as 48 bit PulseDistance using ProtocolConstants")); + Serial.flush(); +# if __INT_WIDTH__ < 32 + IrSender.sendPulseDistanceWidthFromPGMArray_P(&KaseikyoProtocolConstants, &tRawDataPGM[0], 48, NO_REPEATS); // Panasonic is a Kaseikyo variant +# else + IrSender.sendPulseDistanceWidth_P(&KaseikyoProtocolConstants, 0xA010B02002, 48, NO_REPEATS); // Panasonic is a Kaseikyo variant +# endif + + delay(DELAY_AFTER_SEND); + + /* + * Send 2 Panasonic 48 bit codes as Pulse Distance data, once with LSB and once with MSB first + */ + Serial.println(F("Send Panasonic 0xB, 0x10 as 48 bit PulseDistance")); + Serial.println(F(" LSB first")); + Serial.flush(); +# if __INT_WIDTH__ < 32 + IrSender.sendPulseDistanceWidthFromPGMArray(38, 3450, 1700, 450, 1250, 450, 400, &tRawDataPGM[0], 48, + PROTOCOL_IS_LSB_FIRST, 0, NO_REPEATS); +# else + IrSender.sendPulseDistanceWidth(38, 3450, 1700, 450, 1250, 450, 400, 0xA010B02002, 48, PROTOCOL_IS_LSB_FIRST, 0, + NO_REPEATS); +# endif + delay(DELAY_AFTER_SEND); + + // The same with MSB first. Use bit reversed raw data of LSB first part + Serial.println(F(" MSB first")); + IRRawDataType tRawData[4]; +# if __INT_WIDTH__ < 32 + tRawData[0] = 0x40040D00; // MSB of tRawData[0] is sent first + tRawData[1] = 0x805; + IrSender.sendPulseDistanceWidthFromArray(38, 3450, 1700, 450, 1250, 450, 400, &tRawData[0], 48, + PROTOCOL_IS_MSB_FIRST, 0, NO_REPEATS); +# else + IrSender.sendPulseDistanceWidth(38, 3450, 1700, 450, 1250, 450, 400, 0x40040D000805, 48, PROTOCOL_IS_MSB_FIRST, 0, + NO_REPEATS); +# endif + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send 72 bit PulseDistance 0x5A AFEDCBA9 87654321 LSB first")); + Serial.flush(); +# if __INT_WIDTH__ < 32 + tRawData[0] = 0x87654321; // LSB of tRawData[0] is sent first + tRawData[1] = 0xAFEDCBA9; + tRawData[2] = 0x5A; + IrSender.sendPulseDistanceWidthFromArray(38, 8900, 4450, 550, 1700, 550, 600, &tRawData[0], 72, PROTOCOL_IS_LSB_FIRST, 0, + NO_REPEATS); +# else + tRawData[0] = 0xAFEDCBA987654321; + tRawData[1] = 0x5A; // LSB of tRawData[0] is sent first + IrSender.sendPulseDistanceWidthFromArray(38, 8900, 4450, 550, 1700, 550, 600, &tRawData[0], 72, PROTOCOL_IS_LSB_FIRST, 0, + NO_REPEATS); +# endif + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send 52 bit PulseDistanceWidth 0xDCBA9 87654321 LSB first")); + Serial.flush(); + // Real PulseDistanceWidth (constant bit length) does not require a stop bit +# if __INT_WIDTH__ < 32 + IrSender.sendPulseDistanceWidthFromArray(38, 300, 600, 600, 300, 300, 600, &tRawData[0], 52, + PROTOCOL_IS_LSB_FIRST, 0, 0); +# else + IrSender.sendPulseDistanceWidth(38, 300, 600, 600, 300, 300, 600, 0xDCBA987654321, 52, PROTOCOL_IS_LSB_FIRST, 0, 0); +# endif + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send ASCII 7 bit PulseDistanceWidth LSB first")); + Serial.flush(); + // Real PulseDistanceWidth (constant bit length) does theoretically not require a stop bit, but we know the stop bit from serial transmission + IrSender.sendPulseDistanceWidth(38, 6000, 500, 500, 1500, 1500, 500, sCommand, 7, PROTOCOL_IS_LSB_FIRST, 0, 0); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Sony12 as PulseWidth LSB first")); + Serial.flush(); + uint32_t tData = (uint32_t) sAddress << 7 | (sCommand & 0x7F); + IrSender.sendPulseDistanceWidth(38, 2400, 600, 1200, 600, 600, 600, tData, SIRCS_12_PROTOCOL, PROTOCOL_IS_LSB_FIRST, 0, 0); + delay(DELAY_AFTER_SEND); +#endif // FLASHEND >= 0x3FFF ... + + Serial.println(F("Send 32 bit PulseWidth 0x87654321 LSB first")); + Serial.flush(); + // Real PulseDistanceWidth (constant bit length) does not require a stop bit + IrSender.sendPulseDistanceWidth(38, 1000, 500, 600, 300, 300, 300, 0x87654321, 32, PROTOCOL_IS_LSB_FIRST, 0, 0); + delay(DELAY_AFTER_SEND); + } + + Serial.println(F("Send Onkyo (NEC with 16 bit command)")); + Serial.flush(); + IrSender.sendOnkyo(sAddress, s16BitCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Apple")); + Serial.flush(); + IrSender.sendApple(sAddress & 0xFF, sCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Panasonic")); + Serial.flush(); + IrSender.sendPanasonic(sAddress & 0xFFF, sCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Kaseikyo with 0x4711 as Vendor ID")); + Serial.flush(); + IrSender.sendKaseikyo(sAddress & 0xFFF, sCommand, sRepeats, 0x4711); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Kaseikyo_Denon variant")); + Serial.flush(); + IrSender.sendKaseikyo_Denon(sAddress & 0xFFF, sCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Denon")); + Serial.flush(); + IrSender.sendDenon(sAddress & 0x1F, sCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Denon/Sharp variant")); + Serial.flush(); + IrSender.sendSharp(sAddress & 0x1F, sCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Sony/SIRCS with 7 command and 5 address bits")); + Serial.flush(); + IrSender.sendSony(sAddress & 0x1F, sCommand & 0x7F, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Sony/SIRCS with 7 command and 8 address bits")); + Serial.flush(); + IrSender.sendSony(sAddress & 0xFF, sCommand, sRepeats, SIRCS_15_PROTOCOL); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Sony/SIRCS with 7 command and 13 address bits")); + Serial.flush(); + IrSender.sendSony(sAddress & 0x1FFF, sCommand & 0x7F, sRepeats, SIRCS_20_PROTOCOL); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Samsung 8 bit command")); + Serial.flush(); + IrSender.sendSamsung(sAddress, sCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Samsung 16 bit command")); + Serial.flush(); + IrSender.sendSamsung(sAddress, s16BitCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Samsung48 16 bit command")); + Serial.flush(); + IrSender.sendSamsung48(sAddress, s16BitCommand, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send RC5")); + Serial.flush(); + IrSender.sendRC5(sAddress & 0x1F, sCommand & 0x3F, sRepeats, true); // 5 address, 6 command bits + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send RC5X with 7.th MSB of command set")); + Serial.flush(); + IrSender.sendRC5(sAddress & 0x1F, (sCommand & 0x3F) + 0x40, sRepeats, true); // 5 address, 7 command bits + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send RC6")); + Serial.flush(); + IrSender.sendRC6(sAddress, sCommand, sRepeats, true); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send RC6A with 14 bit 0x2711 as extra")); + Serial.flush(); + IrSender.sendRC6A(sAddress & 0xFF, sCommand, sRepeats, 0x2711, true); + delay(DELAY_AFTER_SEND); + +#if FLASHEND >= 0x3FFF && ((!defined(RAMEND) && !defined(RAMSIZE)) || (defined(RAMEND) && RAMEND > 0x4FF) || (defined(RAMSIZE) && RAMSIZE > 0x4FF)) // For 16k flash or more, like ATtiny1604. Code does not fit in program memory of ATtiny85 etc. + + Serial.println(F("Send MagiQuest")); + Serial.flush(); + IrSender.sendMagiQuest(0x6BCD0000 | (uint32_t) sAddress, s16BitCommand); // we have 31 bit address + delay(DELAY_AFTER_SEND); + + // Bang&Olufsen must be sent with 455 kHz +// Serial.println(F("Send Bang&Olufsen")); +// Serial.flush(); +// IrSender.sendBangOlufsen(sAddress, sCommand, sRepeats); +// delay(DELAY_AFTER_SEND); + + /* + * Next example how to use the IrSender.write function + */ + IRData IRSendData; + // prepare data + IRSendData.address = sAddress; + IRSendData.command = sCommand; + IRSendData.flags = IRDATA_FLAGS_EMPTY; + + Serial.println(F("Send next protocols with IrSender.write")); + Serial.flush(); + + IRSendData.protocol = JVC;// switch protocol + Serial.print(F("Send ")); + Serial.println(getProtocolString(IRSendData.protocol)); + Serial.flush(); + IrSender.write(&IRSendData, sRepeats); + delay(DELAY_AFTER_SEND); + + IRSendData.command = s16BitCommand;// LG support more than 8 bit command + + IRSendData.protocol = SAMSUNG; + Serial.print(F("Send ")); + Serial.println(getProtocolString(IRSendData.protocol)); + Serial.flush(); + IrSender.write(&IRSendData, sRepeats); + delay(DELAY_AFTER_SEND); + + IRSendData.protocol = LG; + Serial.print(F("Send ")); + Serial.println(getProtocolString(IRSendData.protocol)); + Serial.flush(); + IrSender.write(&IRSendData, sRepeats); + delay(DELAY_AFTER_SEND); + + IRSendData.protocol = BOSEWAVE; + Serial.println(F("Send Bosewave with no address and 8 command bits")); + Serial.flush(); + IrSender.write(&IRSendData, sRepeats); + delay(DELAY_AFTER_SEND); + + IRSendData.protocol = FAST; + Serial.print(F("Send ")); + Serial.println(getProtocolString(IRSendData.protocol)); + Serial.flush(); + IrSender.write(&IRSendData, sRepeats); + delay(DELAY_AFTER_SEND); + + /* + * LEGO is difficult to receive because of its short marks and spaces + */ + Serial.println(F("Send Lego with 2 channel and with 4 command bits")); + Serial.flush(); + IrSender.sendLegoPowerFunctions(sAddress, sCommand, LEGO_MODE_COMBO, true); + delay(DELAY_AFTER_SEND); + +#endif // FLASHEND + /* + * Force buffer overflow + */ + Serial.println(F("Force buffer overflow by sending 700 marks and spaces")); + for (unsigned int i = 0; i < 350; ++i) { + // 400 + 400 should be received as 8/8 and sometimes as 9/7 or 7/9 if compensation by MARK_EXCESS_MICROS is optimal. + // 210 + 540 = 750 should be received as 5/10 or 4/11 if compensation by MARK_EXCESS_MICROS is optimal. + IrSender.mark(210); // 8 pulses at 38 kHz + IrSender.space(540); // to fill up to 750 us + } + delay(DELAY_AFTER_SEND); + + /* + * Increment values + * Also increment address just for demonstration, which normally makes no sense + */ + sAddress += 0x0101; + sCommand += 0x11; + s16BitCommand += 0x1111; + sRepeats++; + // clip repeats at 4 + if (sRepeats > 4) { + sRepeats = 4; + } + + delay(DELAY_AFTER_LOOP); // additional delay at the end of each loop +} + diff --git a/examples/SendLGAirConditionerDemo/PinDefinitionsAndMore.h b/examples/SendLGAirConditionerDemo/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/SendLGAirConditionerDemo/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/LGACSendDemo/LGACSendDemo.md b/examples/SendLGAirConditionerDemo/Readme.md similarity index 96% rename from examples/LGACSendDemo/LGACSendDemo.md rename to examples/SendLGAirConditionerDemo/Readme.md index 62c707316..ad16367c0 100644 --- a/examples/LGACSendDemo/LGACSendDemo.md +++ b/examples/SendLGAirConditionerDemo/Readme.md @@ -14,17 +14,17 @@ === *** === - (0) : Cooling or Heating -- (1) : fixed -- (2) : fixed +- (1) : fixed address +- (2) : fixed address - (3) : special(power, swing, air clean) - (4) : change air flow, temperature, cooling(0)/heating(4) - (5) : temperature ( 15 + (5) = ) - (6) : air flow -- (7) : crc ( 3 + 4 + 5 + 6 ) & B00001111 +- (7) : checksum ( 3 + 4 + 5 + 6 ) & B00001111 -°F = °C Ɨ 1.8 + 32 -°C = (°F āˆ’ 32) / 1.8 +°F = °C Ɨ 1.8 + 32
+°C = (°F - 32) / 1.8 === *** === diff --git a/examples/SendLGAirConditionerDemo/SendLGAirConditionerDemo.ino b/examples/SendLGAirConditionerDemo/SendLGAirConditionerDemo.ino new file mode 100644 index 000000000..a92e45e21 --- /dev/null +++ b/examples/SendLGAirConditionerDemo/SendLGAirConditionerDemo.ino @@ -0,0 +1,153 @@ +/* + * SendLGAirConditionerDemo.cpp + * + * Sending LG air conditioner IR codes controlled by Serial input + * Based on he old IRremote source from https://github.com/chaeplin + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2022 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +/* + * LG2 has different header timing and a shorter bit time + * Known LG remote controls, which uses LG2 protocol are: + * AKB75215403 + * AKB74955603 + * AKB73757604: + */ +//#define USE_LG2_PROTOCOL // Try it if you do not have success with the default LG protocol +#define NUMBER_OF_COMMANDS_BETWEEN_PRINT_OF_MENU 5 + +#if !defined(ARDUINO_ESP32C3_DEV) // This is due to a bug in RISC-V compiler, which requires unused function sections :-(. +#define DISABLE_CODE_FOR_RECEIVER // Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. +#endif + +#define INFO // Deactivate this to save program memory and suppress info output from the LG-AC driver. +//#define DEBUG // Activate this for more output from the LG-AC driver. + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. +#include + +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +#endif + +#include "ac_LG.hpp" + +#define SIZE_OF_RECEIVE_BUFFER 10 +char sRequestString[SIZE_OF_RECEIVE_BUFFER]; + +Aircondition_LG MyLG_Aircondition; + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + +// Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + Serial.println(F("Send IR signals at pin " STR(IR_SEND_PIN))); + + /* + * The IR library setup. That's all! + */ + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin + + Serial.println(); + MyLG_Aircondition.setType(LG_IS_WALL_TYPE); + MyLG_Aircondition.printMenu(&Serial); + + delay(1000); + +// test +// MyLG_Aircondition.sendCommandAndParameter('j', 1); +// delay(5000); +// MyLG_Aircondition.sendCommandAndParameter('f', 3); +// delay(5000); + +} + +void loop() { + static uint8_t sShowmenuConter = 0; + + if (Serial.available()) { + /* + * Get parameters from serial + */ + uint8_t tNumberOfBytesReceived = Serial.readBytesUntil('\n', sRequestString, SIZE_OF_RECEIVE_BUFFER - 1); + // handle CR LF + if (sRequestString[tNumberOfBytesReceived - 1] == '\r') { + tNumberOfBytesReceived--; + } + sRequestString[tNumberOfBytesReceived] = '\0'; // terminate as string + char tCommand = sRequestString[0]; + + /* + * Handle parameter numbers which can be greater 9 + */ + int tParameter = 0; + if (tNumberOfBytesReceived >= 2) { + tParameter = sRequestString[1] - '0'; + if (tCommand == LG_COMMAND_TEMPERATURE || tCommand == LG_COMMAND_SWING || tCommand == LG_COMMAND_SLEEP + || tCommand == LG_COMMAND_TIMER_ON || tCommand == LG_COMMAND_TIMER_OFF) { + tParameter = atoi(&sRequestString[1]); + } + } + + /* + * Print command to send + */ + Serial.println(); + Serial.print(F("Command=")); + Serial.print(tCommand); + if (tParameter != 0) { + Serial.print(F(" Parameter=")); + Serial.print(tParameter); + } + Serial.println(); + + if (!MyLG_Aircondition.sendCommandAndParameter(tCommand, tParameter)) { + Serial.print(F("Error: unknown command or invalid parameter in \"")); + Serial.print(sRequestString); + Serial.println('\"'); + } + + if (sShowmenuConter == 0) { + MyLG_Aircondition.printMenu(&Serial); + sShowmenuConter = NUMBER_OF_COMMANDS_BETWEEN_PRINT_OF_MENU; + } else { + sShowmenuConter--; + } + } + delay(100); +} + diff --git a/examples/SendProntoDemo/PinDefinitionsAndMore.h b/examples/SendProntoDemo/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/SendProntoDemo/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/SendProntoDemo/SendProntoDemo.ino b/examples/SendProntoDemo/SendProntoDemo.ino new file mode 100644 index 000000000..e935d00c1 --- /dev/null +++ b/examples/SendProntoDemo/SendProntoDemo.ino @@ -0,0 +1,107 @@ +/* + * SendProntoDemo.cpp + * + * Example for sending pronto codes with the IRremote library. + * The code used here, sends NEC protocol data. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2022 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +#if !defined(ARDUINO_ESP32C3_DEV) // This is due to a bug in RISC-V compiler, which requires unused function sections :-(. +#define DISABLE_CODE_FOR_RECEIVER // Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. +#endif + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. +#include + +#define NUMBER_OF_REPEATS 3U + +// The first number, here 0000, denotes the type of the signal. 0000 denotes a raw IR signal with modulation. +// The second number, here 006C, denotes a frequency code. 006C corresponds to 1000000/(0x006c * 0.241246) = 38381 Hertz. +// The third and the forth number denote the number of pairs (= half the number of durations) in the start- and the repeat sequence respectively. +const char yamahaVolDown[] +#if defined(__AVR__) +PROGMEM +#endif += "0000 006C 0022 0002 015B 00AD " /* Pronto header + start bit */ + "0016 0016 0016 0041 0016 0016 0016 0041 0016 0041 0016 0041 0016 0041 0016 0016 " /* Lower address byte */ + "0016 0041 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 " /* Upper address byte (inverted at 8 bit mode) */ + "0016 0041 0016 0041 0016 0016 0016 0041 0016 0041 0016 0016 0016 0016 0016 0016 " /* command byte */ + "0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0041 0016 0041 0016 0041 0016 05F7 " /* inverted command byte + stop bit */ + "015B 0057 0016 0E6C"; /* NEC repeat pattern*/ + +IRsend irsend; + +void setup() { + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + Serial.println(F("Send IR signals at pin " STR(IR_SEND_PIN))); + + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin +} + +void loop() { + +#if defined(__AVR__) + Serial.println(F("Sending NEC from PROGMEM: address 0x85, data 0x1B")); + irsend.sendPronto_P(yamahaVolDown, NUMBER_OF_REPEATS); +#else + Serial.println(F("Sending from normal memory")); + irsend.sendPronto(yamahaVolDown, NUMBER_OF_REPEATS); +#endif + + delay(2000); + Serial.println(F("Sending the NEC from PROGMEM using the F()-form: address 0x5, data 0x1A")); + irsend.sendPronto(F("0000 006C 0022 0002 015B 00AD " /* Pronto header + start bit */ + "0016 0016 0016 0041 0016 0016 0016 0041 0016 0041 0016 0041 0016 0041 0016 0041 " /* Lower address byte */ + "0016 0041 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 " /* Upper address byte (inverted at 8 bit mode) */ + "0016 0016 0016 0041 0016 0016 0016 0041 0016 0041 0016 0016 0016 0016 0016 0016 " /* command byte */ + "0016 0041 0016 0016 0016 0041 0016 0016 0016 0016 0016 0041 0016 0041 0016 0041 0016 05F7 " /* inverted command byte + stop bit */ + "015B 0057 0016 0E6C"), /* NEC repeat pattern*/ + NUMBER_OF_REPEATS); + delay(2000); + + // send Nec code acquired by IRreceiveDump.cpp + Serial.println(F("Sending NEC from RAM: address 0xFF00, data 0x15")); + // 006D -> 38029 Hz + irsend.sendPronto("0000 006D 0022 0000 015C 00AB " /* Pronto header + start bit */ + "0017 0015 0017 0015 0017 0015 0017 0015 0017 0015 0017 0015 0017 0015 0017 0015 " /* Lower address byte */ + "0017 003F 0017 003E 0017 003F 0017 003E 0017 003F 0015 003F 0017 003F 0015 003F " /* Upper address byte (inverted at 8 bit mode) */ + "0017 003E 0017 0015 0017 003F 0017 0015 0017 003E 0017 0015 0017 0017 0015 0017 " /* command byte */ + "0017 0015 0017 003E 0017 0015 0017 003F 0015 0017 0017 003E 0017 003F 0015 003F 0017 0806" /* inverted command byte + stop bit */ + , 0); // No repeat possible, because of missing repeat pattern + + delay(5000); +} diff --git a/examples/SendRawDemo/PinDefinitionsAndMore.h b/examples/SendRawDemo/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/SendRawDemo/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/SendRawDemo/SendRawDemo.ino b/examples/SendRawDemo/SendRawDemo.ino new file mode 100644 index 000000000..5b03a6438 --- /dev/null +++ b/examples/SendRawDemo/SendRawDemo.ino @@ -0,0 +1,126 @@ +/* + * SendRawDemo.cpp - demonstrates sending IR codes with sendRaw + * + * This example shows how to send a RAW signal using the IRremote library. + * The example signal is actually a 32 bit NEC signal. + * Protocol=NEC Address=0x4 Command=0x18 Raw-Data=0xE718FB04 32 bits LSB first + * + * If it is a supported protocol, it is more efficient to use the protocol send function + * (here sendNEC) to send the signal. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +#if !defined(ARDUINO_ESP32C3_DEV) // This is due to a bug in RISC-V compiler, which requires unused function sections :-(. +#define DISABLE_CODE_FOR_RECEIVER // Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. +#endif + +//#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. +//#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition +//#define NO_LED_FEEDBACK_CODE // Saves 566 bytes program memory +//#define USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN // Use or simulate open drain output mode at send pin. Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin! + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. +#include + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + delay(4000); // To be able to connect Serial monitor after reset or power up and before first print out. Do not wait for an attached Serial Monitor! +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + Serial.println(F("Send IR signals at pin " STR(IR_SEND_PIN))); + + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin +} + +/* + * NEC address=0x04 (0xFB04), command=0x18 (0xE718) + * + * This is the raw data array in byte format. + * The uint8_t/byte elements contain the number of ticks in 50 us. + * The uint16_t format contains the (number of ticks * 50) if generated by IRremote, + * so the uint16_t format has exact the same resolution but requires double space. + * With the uint16_t format, you are able to modify the timings to meet the standards, + * e.g. use 560 (instead of 11 * 50) for NEC or use 432 for Panasonic. But in this cases, + * you better use the timing generation functions e.g. sendNEC() directly. + */ +const uint8_t rawDataP[] PROGMEM += { 180, 90 /*Start bit*/, 11, 11, 11, 11, 11, 34, 11, 11/*0010 0x4 of 8 bit address LSB first*/, 11, 11, 11, 11, 11, 11, 11, + 11/*0000*/, 11, 34, 11, 34, 11, 11, 11, 34/*1101 0xB*/, 11, 34, 11, 34, 11, 34, 11, 34/*1111*/, 11, 11, 11, 11, 11, 11, 11, + 34/*0001 0x08 of command LSB first*/, 11, 34, 11, 11, 11, 11, 11, 11/*1000 0x01*/, 11, 34, 11, 34, 11, 34, 11, + 11/*1110 Inverted 8 of command*/, 11, 11, 11, 34, 11, 34, 11, 34/*0111 inverted 1 of command*/, 11 /*stop bit*/}; + +/* + * The same frame as above. Values are NOT multiple of 50, but are taken from the NEC timing definitions + */ +const uint16_t rawData[] = { 9000, 4500/*Start bit*/, 560, 560, 560, 560, 560, 1690, 560, + 560/*0010 0x4 of 8 bit address LSB first*/, 560, 560, 560, 560, 560, 560, 560, 560/*0000*/, 560, 1690, 560, 1690, 560, 560, + 560, 1690/*1101 0xB - inverted 0x04*/, 560, 1690, 560, 1690, 560, 1690, 560, 1690/*1111 - inverted 0*/, 560, 560, 560, 560, + 560, 560, 560, 1690/*0001 0x08 of command LSB first*/, 560, 1690, 560, 560, 560, 560, 560, 560/*1000 0x01*/, 560, 1690, 560, + 1690, 560, 1690, 560, 560/*1110 Inverted 8 of command*/, 560, 560, 560, 1690, 560, 1690, 560, + 1690/*1111 inverted 0 of command*/, 560 /*stop bit*/}; // Using exact NEC timing + +void loop() { + +#if FLASHEND > 0x1FFF // For more than 8k flash => not for ATtiny85 etc. + /* + * Send hand crafted data from RAM + */ + Serial.println(F("Send NEC 8 bit address=0x04 (0xFB04) and command 0x18 (0xE718) with exact timing (16 bit array format)")); + Serial.flush(); + IrSender.sendRaw(rawData, sizeof(rawData) / sizeof(rawData[0]), NEC_KHZ); // Note the approach used to automatically calculate the size of the array. + + delay(1000); // delay must be greater than 8 ms (RECORD_GAP_MICROS), otherwise the receiver sees it as one long signal + +#endif + + /* + * Send byte data direct from FLASH + * Note the approach used to automatically calculate the size of the array. + */ + Serial.println(F("Send NEC 8 bit address 0x04 and command 0x18 with (50 us) tick resolution timing (8 bit array format) ")); + Serial.flush(); + IrSender.sendRaw_P(rawDataP, sizeof(rawDataP) / sizeof(rawDataP[0]), NEC_KHZ); + + delay(1000); // delay must be greater than 8 ms (RECORD_GAP_MICROS), otherwise the receiver sees it as one long signal + + /* + * Send the same frame using NEC encoder + */ + Serial.println(F("Send NEC 16 bit address 0x04, 8 bit command 0x18 with NEC encoder")); + Serial.flush(); + IrSender.sendNEC(0x04, 0x18, 0); + + delay(3000); +} diff --git a/examples/SimpleReceiver/PinDefinitionsAndMore.h b/examples/SimpleReceiver/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/SimpleReceiver/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/SimpleReceiver/SimpleReceiver.ino b/examples/SimpleReceiver/SimpleReceiver.ino new file mode 100644 index 000000000..95eaee8c7 --- /dev/null +++ b/examples/SimpleReceiver/SimpleReceiver.ino @@ -0,0 +1,130 @@ +/* + * SimpleReceiver.cpp + * + * Demonstrates receiving ONLY NEC protocol IR codes with IRremote + * If no protocol is defined, all protocols (except Bang&Olufsen) are active. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2025 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +#include + +/* + * Specify which protocol(s) should be used for decoding. + * If no protocol is defined, all protocols (except Bang&Olufsen) are active. + * This must be done before the #include + */ +//#define DECODE_DENON // Includes Sharp +//#define DECODE_JVC +//#define DECODE_KASEIKYO +//#define DECODE_PANASONIC // alias for DECODE_KASEIKYO +//#define DECODE_LG +//#define DECODE_NEC // Includes Apple and Onkyo. To enable all protocols , just comment/disable this line. +//#define DECODE_SAMSUNG +//#define DECODE_SONY +//#define DECODE_RC5 +//#define DECODE_RC6 + +//#define DECODE_BOSEWAVE +//#define DECODE_LEGO_PF +//#define DECODE_MAGIQUEST +//#define DECODE_WHYNTER +//#define DECODE_FAST + +//#define DECODE_DISTANCE_WIDTH // Universal decoder for pulse distance width protocols +//#define DECODE_HASH // special decoder for all protocols + +//#define DECODE_BEO // This protocol must always be enabled manually, i.e. it is NOT enabled if no protocol is defined. It prevents decoding of SONY! + +//#define DEBUG // Activate this for lots of lovely debug output from the decoders. + +//#define RAW_BUFFER_LENGTH 750 // For air condition remotes it may require up to 750. Default is 200. + +/* + * This include defines the actual pin number for pins like IR_RECEIVE_PIN, IR_SEND_PIN for many different boards and architectures + */ +#include "PinDefinitionsAndMore.h" +#include // include the library + +void setup() { + Serial.begin(115200); + + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); +} + +void loop() { + /* + * Check if received data is available and if yes, try to decode it. + * Decoded result is in the IrReceiver.decodedIRData structure. + * + * E.g. command is in IrReceiver.decodedIRData.command + * address is in command is in IrReceiver.decodedIRData.address + * and up to 32 bit raw data in IrReceiver.decodedIRData.decodedRawData + */ + if (IrReceiver.decode()) { + + /* + * Print a summary of received data + */ + if (IrReceiver.decodedIRData.protocol == UNKNOWN) { + Serial.println(F("Received noise or an unknown (or not yet enabled) protocol")); + // We have an unknown protocol here, print extended info + IrReceiver.printIRResultRawFormatted(&Serial, true); + + IrReceiver.resume(); // Do it here, to preserve raw data for printing with printIRResultRawFormatted() + } else { + IrReceiver.resume(); // Early enable receiving of the next IR frame + + IrReceiver.printIRResultShort(&Serial); + IrReceiver.printIRSendUsage(&Serial); + } + Serial.println(); + + /* + * Finally, check the received data and perform actions according to the received command + */ + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) { + Serial.println(F("Repeat received. Here you can repeat the same action as before.")); + } else { + if (IrReceiver.decodedIRData.command == 0x10) { + Serial.println(F("Received command 0x10.")); + // do something + } else if (IrReceiver.decodedIRData.command == 0x11) { + Serial.println(F("Received command 0x11.")); + // do something else + } + } + } +} diff --git a/examples/SimpleReceiverForHashCodes/PinDefinitionsAndMore.h b/examples/SimpleReceiverForHashCodes/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/SimpleReceiverForHashCodes/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/SimpleReceiverForHashCodes/SimpleReceiverForHashCodes.ino b/examples/SimpleReceiverForHashCodes/SimpleReceiverForHashCodes.ino new file mode 100644 index 000000000..91819446b --- /dev/null +++ b/examples/SimpleReceiverForHashCodes/SimpleReceiverForHashCodes.ino @@ -0,0 +1,101 @@ +/* + * SimpleReceiverForHashCodes.cpp + * + * Demonstrates receiving hash codes of unknown protocols with IRremote + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. + +/* + * Specify which protocol(s) should be used for decoding. + * This must be done before the #include + */ +#define DECODE_HASH // special decoder for all protocols +#if !defined(RAW_BUFFER_LENGTH) +# if !((defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF)) +#define RAW_BUFFER_LENGTH 1000 // Especially useful for unknown and probably long protocols +# endif +#endif +//#define DEBUG // Activate this for lots of lovely debug output from the decoders. + +/* + * This include defines the actual pin number for pins like IR_RECEIVE_PIN, IR_SEND_PIN for many different boards and architectures + */ +#include "PinDefinitionsAndMore.h" +#include // include the library + +void setup() { + Serial.begin(115200); + + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + + Serial.println(F("Ready to receive unknown IR signals at pin " STR(IR_RECEIVE_PIN) " and decode it with hash decoder.")); +} + +void loop() { + /* + * Check if received data is available and if yes, try to decode it. + * Decoded hash result is in IrReceiver.decodedIRData.decodedRawData + */ + if (IrReceiver.available()) { + IrReceiver.initDecodedIRData(); // is required, if we do not call decode(); + IrReceiver.decodeHash(); + IrReceiver.resume(); // Early enable receiving of the next IR frame + /* + * Print a summary and then timing of received data + */ + IrReceiver.printIRResultShort(&Serial); + IrReceiver.printIRResultRawFormatted(&Serial, true); + + Serial.println(); + + /* + * Finally, check the received data and perform actions according to the received command + */ + auto tDecodedRawData = IrReceiver.decodedIRData.decodedRawData; // uint32_t on 8 and 16 bit CPUs and uint64_t on 32 and 64 bit CPUs + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) { + Serial.println(F("Repeat received. Here you can repeat the same action as before.")); + } else { + Serial.print(F("Raw data received are 0x")); + Serial.println(tDecodedRawData); + + if (IrReceiver.decodedIRData.command == 0x10) { + // do something + } else if (IrReceiver.decodedIRData.command == 0x11) { + // do something else + } + } + } +} diff --git a/examples/SimpleReceiverWithCallback/PinDefinitionsAndMore.h b/examples/SimpleReceiverWithCallback/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/SimpleReceiverWithCallback/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/SimpleReceiverWithCallback/SimpleReceiverWithCallback.ino b/examples/SimpleReceiverWithCallback/SimpleReceiverWithCallback.ino new file mode 100644 index 000000000..5489dbc81 --- /dev/null +++ b/examples/SimpleReceiverWithCallback/SimpleReceiverWithCallback.ino @@ -0,0 +1,155 @@ +/* + * SimpleReceiverWithCallback.cpp + * + * Demonstrates receiving NEC IR codes with IRrecv + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2022 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +/* + * Specify which protocol(s) should be used for decoding. + * If no protocol is defined, all protocols (except Bang&Olufsen) are active. + * This must be done before the #include + */ +//#define DECODE_DENON // Includes Sharp +//#define DECODE_JVC +//#define DECODE_KASEIKYO +//#define DECODE_PANASONIC // alias for DECODE_KASEIKYO +//#define DECODE_LG +#define DECODE_NEC // Includes Apple and Onkyo +//#define DECODE_SAMSUNG +//#define DECODE_SONY +//#define DECODE_RC5 +//#define DECODE_RC6 + +//#define DECODE_BOSEWAVE +//#define DECODE_LEGO_PF +//#define DECODE_MAGIQUEST +//#define DECODE_WHYNTER +//#define DECODE_FAST + +//#define DECODE_DISTANCE_WIDTH // Universal decoder for pulse distance width protocols +//#define DECODE_HASH // special decoder for all protocols + +//#define DECODE_BEO // This protocol must always be enabled manually, i.e. it is NOT enabled if no protocol is defined. It prevents decoding of SONY! + +//#define DEBUG // Activate this for lots of lovely debug output from the decoders. + +//#define RAW_BUFFER_LENGTH 750 // For air condition remotes it may require up to 750. Default is 200. + +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. +#include + +/* + * For callback + */ +#define PROCESS_IR_RESULT_IN_MAIN_LOOP +#if defined(PROCESS_IR_RESULT_IN_MAIN_LOOP) || defined(ARDUINO_ARCH_MBED) || defined(ESP32) +volatile bool sIRDataJustReceived = false; +#endif +void ReceiveCompleteCallbackHandler(); + +void setup() { + Serial.begin(115200); + + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + IrReceiver.registerReceiveCompleteCallback(ReceiveCompleteCallbackHandler); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); +} + +void loop() { + /* + * Print in loop (interrupts are enabled here) if received data is available. + */ + if (sIRDataJustReceived) { + // Print a summary of received data + if (IrReceiver.decodedIRData.protocol == UNKNOWN) { + Serial.println(F("Received noise or an unknown (or not yet enabled) protocol")); + /* + * We have an unknown protocol here, print more info. + * !!!Attention!!! This prints incorrect values, if we are late (not in this simple example :-)) + * and the the first mark of the next (repeat) data was yet received + */ + IrReceiver.printIRResultRawFormatted(&Serial, true); // + } else { + IrReceiver.printIRResultShort(&Serial); + IrReceiver.printIRSendUsage(&Serial); + } + Serial.println(); + } +} + +/* + * Callback function + * Here we know, that data is available. + * This function is executed in ISR (Interrupt Service Routine) context (interrupts are blocked here). + * Make it short and fast and keep in mind, that you can not use delay(), prints longer than print buffer size etc., + * because they require interrupts enabled to return. + * In order to enable other interrupts you can call sei() (enable interrupt again) after evaluating/copying data. + * Good practice, but somewhat more complex, is to copy relevant data and signal receiving to main loop. + */ +#if defined(ESP32) || defined(ESP8266) +IRAM_ATTR +# endif +void ReceiveCompleteCallbackHandler() { + IrReceiver.decode(); // fill IrReceiver.decodedIRData + /* + * Enable receiving of the next value. + */ + IrReceiver.resume(); + + /* + * Check the received data and perform actions according to the received command + * Decoded result is in the IrReceiver.decodedIRData structure. + * + * E.g. command is in IrReceiver.decodedIRData.command, + * address is in command is in IrReceiver.decodedIRData.address + * and up to 32 bit raw data in IrReceiver.decodedIRData.decodedRawData + */ + if (IrReceiver.decodedIRData.command == 0x10) { + // do something SHORT here + } else if (IrReceiver.decodedIRData.command == 0x11) { + // do something SHORT here too + } + + /* + * Set flag to trigger printing of results in main loop, + * since printing should not be done in a callback function + * running in ISR (Interrupt Service Routine) context where interrupts are disabled. + */ + sIRDataJustReceived = true; + +} diff --git a/examples/SimpleSender/PinDefinitionsAndMore.h b/examples/SimpleSender/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/SimpleSender/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/SimpleSender/SimpleSender.ino b/examples/SimpleSender/SimpleSender.ino new file mode 100644 index 000000000..c6d97d62d --- /dev/null +++ b/examples/SimpleSender/SimpleSender.ino @@ -0,0 +1,95 @@ +/* + * SimpleSender.cpp + * + * Demonstrates sending IR codes in standard format with address and command + * An extended example for sending can be found as SendDemo. + * Sending IR codes using several pins for sending is implements in the MultipleSendPins example. + * + * Copyright (C) 2020-2025 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * MIT License + */ +#include + +#if !defined(ARDUINO_ESP32C3_DEV) // This is due to a bug in RISC-V compiler, which requires unused function sections :-(. +#define DISABLE_CODE_FOR_RECEIVER // Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. +#endif +//#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. +//#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition + +/* + * This include defines the actual pin number for pins like IR_RECEIVE_PIN, IR_SEND_PIN for many different boards and architectures + */ +#include "PinDefinitionsAndMore.h" +#include // include the library + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + Serial.print(F("Send IR signals at pin ")); + Serial.println(IR_SEND_PIN); + + /* + * The IR library setup. That's all! + */ + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin + disableLEDFeedback(); // Disable feedback LED at default feedback LED pin +} + +/* + * Set up the data to be sent. + * For most protocols, the data is build up with a constant 8 (or 16 byte) address + * and a variable 8 bit command. + * There are exceptions like Sony and Denon, which have 5 bit address. + */ +uint8_t sCommand = 0x34; +uint8_t sRepeats = 0; + +void loop() { + /* + * Print current send values + */ + Serial.println(); + Serial.print(F("Send now: address=0x00, command=0x")); + Serial.print(sCommand, HEX); + Serial.print(F(", repeats=")); + Serial.print(sRepeats); + Serial.println(); + + Serial.println(F("Send standard NEC with 8 bit address")); + Serial.flush(); + + // Receiver output for the first loop must be: Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 (32 bits) + IrSender.sendNEC(0x00, sCommand, sRepeats); + + /* + * If you want to send a raw HEX value directly like e.g. 0xCB340102 you must use sendNECRaw() + */ +// Serial.println(F("Send 32 bit LSB 0xCB340102 with NECRaw()")); +// IrSender.sendNECRaw(0xCB340102, sRepeats); + + /* + * If you want to send an "old" MSB HEX value used by IRremote versions before 3.0 like e.g. 0x40802CD3 you must use sendNECMSB() + */ +// Serial.println(F("Send old 32 bit MSB 0x40802CD3 with sendNECMSB()")); +// IrSender.sendNECMSB(0x40802CD3, 32, sRepeats); + + /* + * Increment send values + */ + sCommand += 0x11; + sRepeats++; + // clip repeats at 4 + if (sRepeats > 4) { + sRepeats = 4; + } + + delay(1000); // delay must be greater than 5 ms (RECORD_GAP_MICROS), otherwise the receiver sees it as one long signal +} diff --git a/examples/TinyReceiver/PinDefinitionsAndMore.h b/examples/TinyReceiver/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/TinyReceiver/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/TinyReceiver/TinyReceiver.ino b/examples/TinyReceiver/TinyReceiver.ino new file mode 100644 index 000000000..b31b4657f --- /dev/null +++ b/examples/TinyReceiver/TinyReceiver.ino @@ -0,0 +1,173 @@ +/* + * TinyReceiver.cpp + * + * Small memory footprint and no timer usage! + * + * Receives IR protocol data of NEC protocol using pin change interrupts. + * On complete received IR command the function handleReceivedIRData(uint16_t aAddress, uint8_t aCommand, uint8_t aFlags) + * is called in Interrupt context but with interrupts being enabled to enable use of delay() etc. + * !!!!!!!!!!!!!!!!!!!!!! + * Functions called in interrupt context should be running as short as possible, + * so if you require longer action, save the data (address + command) and handle it in the main loop. + * !!!!!!!!!!!!!!!!!!!!! + * + * The FAST protocol is a proprietary modified JVC protocol without address, with parity and with a shorter header. + * FAST Protocol characteristics: + * - Bit timing is like NEC or JVC + * - The header is shorter, 3156 vs. 12500 + * - No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command, + * leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms. + * - Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance. + * + * + * This file is part of IRMP https://github.com/IRMP-org/IRMP. + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2022-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +#include + +#include "PinDefinitionsAndMore.h" // Set IR_RECEIVE_PIN for different CPU's + +//#define DEBUG // to see if attachInterrupt is used +//#define TRACE // to see the state of the ISR state machine + +/* + * Protocol selection + */ +//#define DISABLE_PARITY_CHECKS // Disable parity checks. Saves 48 bytes of program memory. +//#define USE_EXTENDED_NEC_PROTOCOL // Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value. +//#define USE_ONKYO_PROTOCOL // Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value. +//#define USE_FAST_PROTOCOL // Use FAST protocol instead of NEC / ONKYO. +//#define ENABLE_NEC2_REPEATS // Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat. +/* + * Set compile options to modify the generated code. + */ +//#define DISABLE_PARITY_CHECKS // Disable parity checks. Saves 48 bytes of program memory. +//#define USE_CALLBACK_FOR_TINY_RECEIVER // Call the user provided function "void handleReceivedTinyIRData()" each time a frame or repeat is received. + +#include "TinyIRReceiver.hpp" // include the code + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif + +void setup() { + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + // Wait until Serial Monitor is attached. + // Required for boards using USB code for Serial like Leonardo. + // Is void for USB Serial implementations using external chips e.g. a CH340. + while (!Serial) + ; + // !!! Program will not proceed if no Serial Monitor is attached !!! +#endif + + // Just to know which program is running on my Arduino +#if defined(ESP8266) || defined(ESP32) + Serial.println(); +#endif + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_TINYIR)); + + // Enables the interrupt generation on change of IR input signal + if (!initPCIInterruptForTinyReceiver()) { + Serial.println(F("No interrupt available for pin " STR(IR_RECEIVE_PIN))); // optimized out by the compiler, if not required :-) + } +#if defined(USE_FAST_PROTOCOL) + Serial.println(F("Ready to receive Fast IR signals at pin " STR(IR_RECEIVE_PIN))); +#else + Serial.println(F("Ready to receive NEC IR signals at pin " STR(IR_RECEIVE_PIN))); +#endif +} + +void loop() { + if (TinyReceiverDecode()) { + +#if !defined(USE_FAST_PROTOCOL) + // We have no address at FAST protocol + Serial.print(F("Address=0x")); + Serial.print(TinyIRReceiverData.Address, HEX); + Serial.print(' '); +#endif + + Serial.print(F("Command=0x")); + Serial.print(TinyIRReceiverData.Command, HEX); + if (TinyIRReceiverData.Flags == IRDATA_FLAGS_IS_REPEAT) { + Serial.print(F(" Repeat")); + } + if (TinyIRReceiverData.Flags == IRDATA_FLAGS_PARITY_FAILED) { + Serial.print(F(" Parity failed")); + +#if !defined(USE_EXTENDED_NEC_PROTOCOL) && !defined(USE_ONKYO_PROTOCOL) + Serial.print(F(", try USE_EXTENDED_NEC_PROTOCOL or USE_ONKYO_PROTOCOL")); +#endif + + } + Serial.println(); + } + /* + * Put your code here + */ + + /* + * No resume() required :-) + */ +} + +/* + * Optional code, if you require a callback + */ +#if defined(USE_CALLBACK_FOR_TINY_RECEIVER) +/* + * This is the function, which is called if a complete frame was received + * It runs in an ISR context with interrupts enabled, so functions like delay() etc. should work here + */ +# if defined(ESP8266) || defined(ESP32) +IRAM_ATTR +# endif + +void handleReceivedTinyIRData() { +# if defined(ARDUINO_ARCH_MBED) || defined(ESP32) + /* + * Printing is not allowed in ISR context for any kind of RTOS, so we use the slihjtly more complex, + * but recommended way for handling a callback :-). Copy data for main loop. + * For Mbed we get a kernel panic and "Error Message: Semaphore: 0x0, Not allowed in ISR context" for Serial.print() + * for ESP32 we get a "Guru Meditation Error: Core 1 panic'ed" (we also have an RTOS running!) + */ + // Do something useful here... +# else + // As an example, print very short output, since we are in an interrupt context and do not want to miss the next interrupts of the repeats coming soon + printTinyReceiverResultMinimal(&Serial); +# endif +} +#endif + diff --git a/examples/TinySender/PinDefinitionsAndMore.h b/examples/TinySender/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/TinySender/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/TinySender/TinySender.ino b/examples/TinySender/TinySender.ino new file mode 100644 index 000000000..4af49883b --- /dev/null +++ b/examples/TinySender/TinySender.ino @@ -0,0 +1,135 @@ +/* + * TinySender.cpp + * + * Example for sending using TinyIR. By default sends simultaneously using all supported protocols + * To use a single protocol, simply delete or comment out all unneeded protocols in the main loop + * Program size is significantly reduced when using a single protocol + * For example, sending only 8 bit address and command NEC codes saves 780 bytes program memory and 26 bytes RAM compared to SimpleSender, + * which does the same, but uses the IRRemote library (and is therefore much more flexible). + * + * + * The FAST protocol is a proprietary modified JVC protocol without address, with parity and with a shorter header. + * FAST Protocol characteristics: + * - Bit timing is like NEC or JVC + * - The header is shorter, 3156 vs. 12500 + * - No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command, + * leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms. + * - Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance. + * + * + * This file is part of IRMP https://github.com/IRMP-org/IRMP. + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2022-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#include + +#include "PinDefinitionsAndMore.h" // Set IR_SEND_PIN for different CPU's + +#include "TinyIRSender.hpp" + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_TINYIR)); + Serial.print(F("Send IR signals at pin ")); + Serial.println(IR_SEND_PIN); +} + +/* + * Set up the data to be sent. + * The compiler is intelligent and removes the code for 16 bit address handling if we call it with an uint8_t address :-). + * Using an uint16_t address or data requires additional 28 bytes program memory for NEC and 56 bytes program memory for FAST. + */ +uint8_t sAddress = 0x02; +//uint16_t sAddress = 0x02; +uint8_t sCommand = 0x34; +//uint16_t sCommand = 0x34; +uint8_t sRepeats = 0; + +void loop() { + /* + * Print current send values + */ + Serial.println(); + Serial.print(F("Send now:")); + Serial.print(F(" address=0x")); + Serial.print(sAddress, HEX); + Serial.print(F(" command=0x")); + Serial.print(sCommand, HEX); + Serial.print(F(" repeats=")); + Serial.print(sRepeats); + Serial.println(); + + // Send with FAST + // No address and only 16 bits of data, interpreted as 8 bit command and 8 bit inverted command for parity checking + Serial.println(F("Send FAST with 8 bit command")); + Serial.flush(); + sendFAST(IR_SEND_PIN, sCommand, sRepeats); + + // Send with NEC + // NEC uses 8 bit address and 8 bit command each with 8 bit inverted parity checks + // However, sendNEC will accept 16 bit address and commands too (but remove the parity checks) + Serial.println(F("Send NEC with 8 bit address and command")); + Serial.flush(); + sendNEC(IR_SEND_PIN, sAddress, sCommand, sRepeats); + + // Send with Extended NEC + // Like NEC, but the address is forced 16 bits with no parity check + Serial.println(F("Send ExtendedNEC with 16 bit address and 8 bit command")); + Serial.flush(); + sendExtendedNEC(IR_SEND_PIN, sAddress, sCommand, sRepeats); + + // Send with ONKYO + // Like NEC, but both the address and command are forced 16 bits with no parity check + Serial.println(F("Send ONKYO with 16 bit address and command")); + Serial.flush(); + sendONKYO(IR_SEND_PIN, sAddress, sCommand, sRepeats); + + // Send with NEC2 + // Instead of sending the NEC special repeat code, sends the full original frame for repeats + // Sending NEC2 is done by setting the optional bool NEC2Repeats argument to true (defaults to false) + // sendExtendedNEC and sendONKYO also support the NEC2Repeats argument for full frame repeats (not demonstrated here) + Serial.println(F("Send NEC2 with 8 bit address and command and original frame repeats")); + Serial.flush(); + sendNEC(IR_SEND_PIN, sAddress, sCommand, sRepeats, true); + + /* + * Increment send values + * Also increment address just for demonstration, which normally makes no sense + */ + sAddress += 0x0101; + sCommand += 0x11; + sRepeats++; + // clip repeats at 4 + if (sRepeats > 4) { + sRepeats = 4; + } + + delay(1000); // delay must be greater than 5 ms (RECORD_GAP_MICROS), otherwise the receiver sees it as one long signal +} diff --git a/examples/UnitTest/PinDefinitionsAndMore.h b/examples/UnitTest/PinDefinitionsAndMore.h new file mode 100644 index 000000000..b13414f56 --- /dev/null +++ b/examples/UnitTest/PinDefinitionsAndMore.h @@ -0,0 +1,357 @@ +/* + * PinDefinitionsAndMore.h + * + * Contains pin definitions for IRremote examples for various platforms + * as well as definitions for feedback LED and tone() and includes + * + * Copyright (C) 2021-2023 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * Arduino-IRremote is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* + * Pin mapping table for different platforms + * + * Platform IR input IR output Tone Core/Pin schema + * -------------------------------------------------------------- + * DEFAULT/AVR 2 3 4 Arduino + * ATtinyX5 0|PB0 4|PB4 3|PB3 ATTinyCore + * ATtiny167 3|PA3 2|PA2 7|PA7 ATTinyCore + * ATtiny167 9|PA3 8|PA2 5|PA7 Digispark original core + * ATtiny84 |PB2 |PA4 |PA3 ATTinyCore + * ATtiny88 3|PD3 4|PD4 9|PB1 ATTinyCore + * ATtiny3216 14|PA1 15|PA2 16|PA3 MegaTinyCore + * ATtiny1604 2 3|PA5 % + * ATtiny816 14|PA1 16|PA3 1|PA5 MegaTinyCore + * ATtiny1614 8|PA1 10|PA3 1|PA5 MegaTinyCore + * MKR* 1 3 4 + * SAMD 2 3 4 + * ESP8266 14|D5 12|D6 % + * ESP32 15 4 27 + * ESP32-C3 2 3 4 + * BluePill PA6 PA7 PA3 + * APOLLO3 11 12 5 + * RP2040 3|GPIO15 4|GPIO16 5|GPIO17 + */ +//#define _IR_MEASURE_TIMING // For debugging purposes. + +#if defined(__AVR__) +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) // Digispark board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // TX is at pin 2 - Available as Arduino library "ATtinySerialOut". Saves 700 bytes program memory and 70 bytes RAM for ATtinyCore. +#define IR_RECEIVE_PIN PIN_PB0 +#define IR_SEND_PIN PIN_PB4 // Pin 2 is serial output with ATtinySerialOut. Pin 1 is internal LED and Pin3 is USB+ with pullup on Digispark board. +#define TONE_PIN PIN_PB3 +#define _IR_TIMING_TEST_PIN PIN_PB3 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark pro board +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" +// For ATtiny167 Pins PB6 and PA3 are usable as interrupt source. +# if defined(ARDUINO_AVR_DIGISPARKPRO) +// For use with Digispark original core +#define IR_RECEIVE_PIN 9 // PA3 - on Digispark board labeled as pin 9 +//#define IR_RECEIVE_PIN 14 // PB6 / INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN 8 // PA2 - on Digispark board labeled as pin 8 +#define TONE_PIN 5 // PA7 - on Digispark board labeled as pin 5 +#define _IR_TIMING_TEST_PIN 10 // PA4 +# else +// For use with ATTinyCore +#define IR_RECEIVE_PIN PIN_PA3 // On Digispark board labeled as pin 9 - INT0 is connected to USB+ on DigisparkPro boards +#define IR_SEND_PIN PIN_PA2 // On Digispark board labeled as pin 8 +#define TONE_PIN PIN_PA7 // On Digispark board labeled as pin 5 +# endif + +# elif defined(__AVR_ATtiny84__) // For use with ATTinyCore +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +#define IR_RECEIVE_PIN PIN_PB2 // INT0 +#define IR_SEND_PIN PIN_PA4 +#define TONE_PIN PIN_PA3 +#define _IR_TIMING_TEST_PIN PIN_PA5 + +# elif defined(__AVR_ATtiny88__) // MH-ET Tiny88 board. For use with ATTinyCore. +#include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut". Saves 128 bytes program memory. +// Pin 6 is TX, pin 7 is RX +#define IR_RECEIVE_PIN PIN_PD3 // 3 - INT1 +#define IR_SEND_PIN PIN_PD4 // 4 +#define TONE_PIN PIN_PB1 // 9 +#define _IR_TIMING_TEST_PIN PIN_PB0 // 8 + +# elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // For use with megaTinyCore +// Tiny Core Dev board +// https://www.tindie.com/products/xkimi/tiny-core-16-dev-board-attiny1616/ - Out of Stock +// https://www.tindie.com/products/xkimi/tiny-core-32-dev-board-attiny3217/ - Out of Stock +#define IR_RECEIVE_PIN PIN_PA1 // 14 use 18 instead of PIN_PA1 for TinyCore32 +#define IR_SEND_PIN PIN_PA2 // 15, 19 for TinyCore32 +#define TONE_PIN PIN_PA3 // 16, 20 for TinyCore32 +#define APPLICATION_PIN PIN_PC3 // 13, PIN_PA0 is RESET +#undef LED_BUILTIN // No LED available on the TinyCore 32 board, take the one on the programming board which is connected to the DAC output +#define LED_BUILTIN PIN_PA6 // use 2 instead of PIN_PA6 for TinyCore32 + +# elif defined(__AVR_ATtiny816__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 14 +#define IR_SEND_PIN PIN_PA1 // 16 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 +#undef LED_BUILTIN // No LED available, take the one which is connected to the DAC output +#define LED_BUILTIN PIN_PB5 // 4 + +# elif defined(__AVR_ATtiny1614__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA1 // 8 +#define IR_SEND_PIN PIN_PA3 // 10 +#define TONE_PIN PIN_PA5 // 1 +#define APPLICATION_PIN PIN_PA4 // 0 + +# elif defined(__AVR_ATtiny1604__) // For use with megaTinyCore +#define IR_RECEIVE_PIN PIN_PA6 // 2 - To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN PIN_PA7 // 3 +#define APPLICATION_PIN PIN_PB2 // 5 + +#define tone(...) void() // Define as void, since TCB0_INT_vect is also used by tone() +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 13 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# else // Default as for ATmega328 like on Uno, Nano, Leonardo, Teensy 2.0 etc. +#define IR_RECEIVE_PIN 2 // To be compatible with interrupt example, pin 2 is chosen here. +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit +// We have no built in LED at pin 13 -> reuse RX LED +#undef LED_BUILTIN +#define LED_BUILTIN LED_BUILTIN_RX +# endif +# endif // defined(__AVR_ATtiny25__)... + +#elif defined(ARDUINO_ARCH_RENESAS_UNO) // Uno R4 +// To be compatible with Uno R3. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ESP8266) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D4) is active LOW +#define IR_RECEIVE_PIN 14 // D5 +#define IR_SEND_PIN 12 // D6 - D4/pin 2 is internal LED +#define _IR_TIMING_TEST_PIN 2 // D4 +#define APPLICATION_PIN 13 // D7 + +#define tone(...) void() // tone() inhibits receive timer +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it# + +#elif defined(ARDUINO_NOLOGO_ESP32C3_SUPER_MINI) +#define FEEDBACK_LED_IS_ACTIVE_LOW // The LED on my board (D8) is active LOW +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 10 + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(ARDUINO_ESP32C3_DEV) +#define NO_LED_FEEDBACK_CODE // The WS2812 on pin 8 of AI-C3 board crashes if used as receive feedback LED, other I/O pins are working... +#define IR_RECEIVE_PIN 6 +#define IR_SEND_PIN 7 +#define TONE_PIN 9 +#define APPLICATION_PIN 10 + +#elif defined(ESP32) +#include + +// tone() is included in ESP32 core since 2.0.2 +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif +#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) +#define TONE_LEDC_CHANNEL 1 // Using channel 1 makes tone() independent of receiving timer -> No need to stop receiving timer. +void tone(uint8_t aPinNumber, unsigned int aFrequency){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); +} +void tone(uint8_t aPinNumber, unsigned int aFrequency, unsigned long aDuration){ + ledcAttachPin(aPinNumber, TONE_LEDC_CHANNEL); + ledcWriteTone(TONE_LEDC_CHANNEL, aFrequency); + delay(aDuration); + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +void noTone(uint8_t aPinNumber){ + ledcWriteTone(TONE_LEDC_CHANNEL, 0); +} +#endif // ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(2, 0, 2) + +#define IR_RECEIVE_PIN 15 // D15 +#define IR_SEND_PIN 4 // D4 +#define TONE_PIN 27 // D27 25 & 26 are DAC0 and 1 +#define APPLICATION_PIN 16 // RX2 pin + +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_STM32F1) // BluePill +// Timer 3 blocks PA6, PA7, PB0, PB1 for use by Servo or tone() +#define IR_RECEIVE_PIN PA6 +#define IR_RECEIVE_PIN_STRING "PA6" +#define IR_SEND_PIN PA7 +#define IR_SEND_PIN_STRING "PA7" +#define TONE_PIN PA3 +#define _IR_TIMING_TEST_PIN PA5 +#define APPLICATION_PIN PA2 +#define APPLICATION_PIN_STRING "PA2" +# if defined(ARDUINO_GENERIC_STM32F103C) || defined(ARDUINO_BLUEPILL_F103C8) +// BluePill LED is active low +#define FEEDBACK_LED_IS_ACTIVE_LOW +# endif + +#elif defined(ARDUINO_ARCH_APOLLO3) // Sparkfun Apollo boards +#define IR_RECEIVE_PIN 11 +#define IR_SEND_PIN 12 +#define TONE_PIN 5 + +#elif defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_MBED_NANO) // Arduino Nano 33 BLE and Arduino Nano Connect layout for MBED +// Must be before ARDUINO_ARCH_RP2040, since it is the layout for the MBED core of Arduino Nano Connect +#define IR_RECEIVE_PIN 3 // GPIO15 Start with pin 3 since pin 2|GPIO25 is connected to LED on Pi pico +#define IR_SEND_PIN 4 // GPIO16 +#define TONE_PIN 5 +#define APPLICATION_PIN 6 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 7 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 8 + +#elif defined(ARDUINO_ARCH_RP2040) // Arduino Nano Connect, Pi Pico with arduino-pico core https://github.com/earlephilhower/arduino-pico +#define IR_RECEIVE_PIN 15 // GPIO15 to be compatible with the Arduino Nano RP2040 Connect (pin3) +#define IR_SEND_PIN 16 // GPIO16 +#define TONE_PIN 17 +#define APPLICATION_PIN 18 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 19 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 20 + +// If you program the Nano RP2040 Connect with this core, then you must redefine LED_BUILTIN +// and use the external reset with 1 kOhm to ground to enter UF2 mode +#undef LED_BUILTIN +#define LED_BUILTIN 6 + +#elif defined(PARTICLE) // !!!UNTESTED!!! +#define IR_RECEIVE_PIN A4 +#define IR_SEND_PIN A5 // Particle supports multiple pins + +#define LED_BUILTIN D7 + +/* + * 4 times the same (default) layout for easy adaption in the future + */ +#elif defined(TEENSYDUINO) // Teensy 2.0 is handled at default for ATmega328 like on Uno, Nano, Leonardo etc. +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) +# if defined(USE_ARDUINO_MKR_PIN_LAYOUT) +#define IR_RECEIVE_PIN 1 // Pin 2 on MKR is not interrupt capable, see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ +# else +#define IR_RECEIVE_PIN 2 +# endif +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 + +#if !defined(ARDUINO_SAMD_ADAFRUIT) && !defined(ARDUINO_SEEED_XIAO_M0) +// On the Zero and others we switch explicitly to SerialUSB +#define Serial SerialUSB +#endif + +// Definitions for the Chinese SAMD21 M0-Mini clone, which has no led connected to D13/PA17. +// Attention!!! D2 and D4 are swapped on these boards!!! +// If you connect the LED, it is on pin 24/PB11. In this case activate the next two lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 24 // PB11 +// As an alternative you can choose pin 25, it is the RX-LED pin (PB03), but active low.In this case activate the next 3 lines. +//#undef LED_BUILTIN +//#define LED_BUILTIN 25 // PB03 +//#define FEEDBACK_LED_IS_ACTIVE_LOW // The RX LED on the M0-Mini is active LOW + +#elif defined (NRF51) // BBC micro:bit +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define APPLICATION_PIN 1 +#define _IR_TIMING_TEST_PIN 4 + +#define tone(...) void() // no tone() available +#define noTone(a) void() +#define TONE_PIN 42 // Dummy for examples using it + +#else +#warning Board / CPU is not detected using pre-processor symbols -> using default values, which may not fit. Please extend PinDefinitionsAndMore.h. +// Default valued for unidentified boards +#define IR_RECEIVE_PIN 2 +#define IR_SEND_PIN 3 +#define TONE_PIN 4 +#define APPLICATION_PIN 5 +#define ALTERNATIVE_IR_FEEDBACK_LED_PIN 6 // E.g. used for examples which use LED_BUILDIN for example output. +#define _IR_TIMING_TEST_PIN 7 +#endif // defined(ESP8266) + +#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE) || defined(ARDUINO_ARCH_MBED) +#define SEND_PWM_BY_TIMER // We do not have pin restrictions for this CPU's, so lets use the hardware PWM for send carrier signal generation +#else +# if defined(SEND_PWM_BY_TIMER) +#undef IR_SEND_PIN // SendPin is determined by timer! This avoids warnings in IRremote.hpp and IRTimer.hpp +# endif +#endif + +#if !defined (FLASHEND) +#define FLASHEND 0xFFFF // Dummy value for platforms where FLASHEND is not defined +#endif + +/* + * Helper macro for getting a macro definition as string + */ +#if !defined(STR_HELPER) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif diff --git a/examples/UnitTest/UnitTest.ino b/examples/UnitTest/UnitTest.ino new file mode 100644 index 000000000..afdbf4bc7 --- /dev/null +++ b/examples/UnitTest/UnitTest.ino @@ -0,0 +1,927 @@ +/* + * UnitTest.cpp + * + * Demonstrates sending IR codes in standard format with address and command and + * simultaneously receiving. Both values are checked for consistency. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2025 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +#include + +#include "PinDefinitionsAndMore.h" // Define macros for input and output pin etc. + +#if !defined(RAW_BUFFER_LENGTH) +// For air condition remotes it requires 600 (maximum for 2k RAM) to 750. Default is 112 if DECODE_MAGIQUEST is enabled, otherwise 100. +# if (defined(RAMEND) && RAMEND <= 0x4FF) || (defined(RAMSIZE) && RAMSIZE < 0x4FF) +#define RAW_BUFFER_LENGTH 360 +# elif (defined(RAMEND) && RAMEND <= 0x8FF) || (defined(RAMSIZE) && RAMSIZE < 0x8FF) +#define RAW_BUFFER_LENGTH 400 // 400 is OK with Pronto and 1000 is OK without Pronto. 1200 is too much here, because then variables are overwritten. +# endif +#endif + +//#define EXCLUDE_UNIVERSAL_PROTOCOLS // Saves up to 1000 bytes program memory. +//#define EXCLUDE_EXOTIC_PROTOCOLS // Saves around 240 bytes program memory if IrSender.write is used +//#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. +//#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition +#if FLASHEND <= 0x7FFF // For 32k flash or less, like ATmega328 +#define NO_LED_FEEDBACK_CODE // Saves 344 bytes program memory +#endif +// MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding, +//#define USE_MSB_DECODING_FOR_DISTANCE_DECODER +// to compensate for the signal forming of different IR receiver modules. See also IRremote.hpp line 142. +//#define MARK_EXCESS_MICROS 20 // Adapt it to your IR receiver module. 40 is taken for the cheap VS1838 module her, since we have high intensity. + +//#define RECORD_GAP_MICROS 12000 // Default is 8000. Activate it for some LG air conditioner protocols. + +//#define TRACE // For internal usage +//#define DEBUG // Activate this for lots of lovely debug output from the decoders. + +#if FLASHEND >= 0x1FFF // For 8k flash or more, like ATtiny85 +#define DECODE_DENON // Includes Sharp +#define DECODE_KASEIKYO +#define DECODE_PANASONIC // alias for DECODE_KASEIKYO +#define DECODE_NEC // Includes Apple and Onkyo +#endif + +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604 +#define DECODE_JVC +#define DECODE_RC5 +#define DECODE_RC6 + +#define DECODE_DISTANCE_WIDTH // Universal decoder for pulse distance width protocols +#define DECODE_HASH // special decoder for all protocols +#endif + +#if FLASHEND >= 0x7FFF // For 32k flash or more, like ATmega328 +#define DECODE_SONY +#define DECODE_SAMSUNG +#define DECODE_LG + +#define DECODE_BEO // It prevents decoding of SONY (default repeats), which we are not using here. +//#define ENABLE_BEO_WITHOUT_FRAME_GAP // !!!For successful unit testing we must see the warning at ir_BangOlufsen.hpp:100:2!!! +#if defined(DECODE_BEO) +#define RECORD_GAP_MICROS 16000 // Force to get the complete frame including the 3. space of 15 ms in the receive buffer +#define BEO_KHZ 38 // We send and receive Bang&Olufsen with 38 kHz here (instead of 455 kHz). +#endif + +#define DECODE_BOSEWAVE +#define DECODE_MAGIQUEST +#define DECODE_FAST + +//#define DECODE_WHYNTER +//#define DECODE_LEGO_PF +#endif + +//#undef IR_SEND_PIN // enable this, if you need to set send pin programmatically using uint8_t tSendPin below +#include + +#if defined(APPLICATION_PIN) +#define DEBUG_BUTTON_PIN APPLICATION_PIN // if held low, print timing for each received data +#else +#define DEBUG_BUTTON_PIN 6 +#endif + +#define DELAY_AFTER_SEND 1000 +#define DELAY_AFTER_LOOP 5000 + +#if defined(SEND_PWM_BY_TIMER) && !defined(SEND_PWM_DOES_NOT_USE_RECEIVE_TIMER) +#error Unit test cannot run if SEND_PWM_BY_TIMER is enabled i.e. receive timer us also used by send +#endif + +/* + * For callback + */ +volatile bool sDataJustReceived = false; +void ReceiveCompleteCallbackHandler(); + +#if __INT_WIDTH__ < 32 +IRRawDataType const tRawDataPGM[4] PROGMEM = { 0xB02002, 0xA010, 0x0, 0x0 }; // LSB of tRawData[0] is sent first +#endif + +void setup() { + pinMode(DEBUG_BUTTON_PIN, INPUT_PULLUP); + + Serial.begin(115200); + +#if defined(__AVR_ATmega32U4__) || defined(SERIAL_PORT_USBVIRTUAL) || defined(SERIAL_USB) /*stm32duino*/|| defined(USBCON) /*STM32_stm32*/ \ + || defined(SERIALUSB_PID) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_attiny3217) + // Wait until Serial Monitor is attached. + // Required for boards using USB code for Serial like Leonardo. + // Is void for USB Serial implementations using external chips e.g. a CH340. + while (!Serial) + ; + // !!! Program will not proceed if no Serial Monitor is attached !!! +#endif + // Just to know which program is running on my Arduino + Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE)); + + // Start the receiver and if not 3. parameter specified, take LED_BUILTIN pin from the internal boards definition as default feedback LED + IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); + IrReceiver.registerReceiveCompleteCallback(ReceiveCompleteCallbackHandler); + + Serial.print(F("Ready to receive IR signals of protocols: ")); + printActiveIRProtocols(&Serial); +#if defined(IR_RECEIVE_PIN_STRING) + Serial.println(F("at pin " IR_RECEIVE_PIN_STRING)); +#else + Serial.println(F("at pin " STR(IR_RECEIVE_PIN))); +#endif + +#if defined(LED_BUILTIN) && !defined(NO_LED_FEEDBACK_CODE) +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + Serial.print(F("Active low ")); +# endif + Serial.print(F("FeedbackLED at pin ")); + Serial.println(LED_BUILTIN); // Works also for ESP32: static const uint8_t LED_BUILTIN = 8; #define LED_BUILTIN LED_BUILTIN +#endif + + Serial.println(F("Use ReceiveCompleteCallback")); + Serial.println(F("Receive buffer length is " STR(RAW_BUFFER_LENGTH))); + +#if defined(IR_SEND_PIN) + IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h- as send pin and enable feedback LED at default feedback LED pin +# if defined(IR_SEND_PIN_STRING) + Serial.println(F("Send IR signals at pin " IR_SEND_PIN_STRING)); +# else + Serial.println(F("Send IR signals at pin " STR(IR_SEND_PIN))); +# endif +#else + // Here the macro IR_SEND_PIN is not defined or undefined above with #undef IR_SEND_PIN + uint8_t tSendPin = 3; + IrSender.begin(tSendPin, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN);// Specify send pin and enable feedback LED at default feedback LED pin + // You can change send pin later with IrSender.setSendPin(); + + Serial.print(F("Send IR signals at pin ")); + Serial.println(tSendPin); +#endif + +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604 + Serial.print(F("If you connect debug pin ")); +# if defined(APPLICATION_PIN_STRING) + Serial.print(APPLICATION_PIN_STRING); +# else + Serial.print(DEBUG_BUTTON_PIN); +# endif + Serial.println(F(" to ground, raw data is always printed")); + + // For esp32 we use PWM generation by ledcWrite() for each pin. +# if !defined(SEND_PWM_BY_TIMER) + /* + * Print internal software PWM generation info + */ + IrSender.enableIROut(38); // Call it with 38 kHz to initialize the values printed below + Serial.print(F("Send signal mark duration for 38kHz is ")); + Serial.print(IrSender.periodOnTimeMicros); + Serial.print(F(" us, pulse narrowing correction is ")); + Serial.print(IrSender.getPulseCorrectionNanos()); + Serial.print(F(" ns, total period is ")); + Serial.print(IrSender.periodTimeMicros); + Serial.println(F(" us")); +# endif + + // infos for receive + Serial.print(RECORD_GAP_MICROS); + Serial.println(F(" us is the (minimum) gap, after which the start of a new IR packet is assumed")); + Serial.print(MARK_EXCESS_MICROS); + Serial.println(F(" us are subtracted from all marks and added to all spaces for decoding")); +#endif + delay(DELAY_AFTER_SEND); + +} + +void checkReceivedRawData(IRRawDataType aRawData) { + // wait until signal has received + while (!sDataJustReceived) { + }; + sDataJustReceived = false; + + if (IrReceiver.decode()) { +// Print a short summary of received data +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604 + IrReceiver.printIRResultShort(&Serial); + IrReceiver.printIRSendUsage(&Serial); +#else + IrReceiver.printIRResultMinimal(&Serial); +#endif +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604 + if (IrReceiver.decodedIRData.protocol == UNKNOWN || digitalRead(DEBUG_BUTTON_PIN) == LOW) { + // We have an unknown protocol, print more info + IrReceiver.printIRResultRawFormatted(&Serial, true); + } +#endif + if (IrReceiver.decodedIRData.protocol == PULSE_DISTANCE || IrReceiver.decodedIRData.protocol == PULSE_WIDTH) { + if (IrReceiver.decodedIRData.decodedRawData != aRawData) { + Serial.print(F("ERROR: Received data=0x")); +#if (__INT_WIDTH__ < 32) + Serial.print(IrReceiver.decodedIRData.decodedRawData, HEX); +#else + PrintULL::print(&Serial, IrReceiver.decodedIRData.decodedRawData, HEX); +#endif + Serial.print(F(" != sent data=0x")); +#if (__INT_WIDTH__ < 32) + Serial.print(aRawData, HEX); +#else + PrintULL::print(&Serial, aRawData, HEX); +#endif + Serial.println(); + } + } + IrReceiver.resume(); + } else { + Serial.println(F("No data received")); + } + Serial.println(); +} + +#if defined(DECODE_DISTANCE_WIDTH) +void checkReceivedArray(IRRawDataType *aRawDataArrayPointer, uint8_t aArraySize) { + // wait until signal has received + while (!sDataJustReceived) { + }; + sDataJustReceived = false; + + if (IrReceiver.decode()) { +// Print a short summary of received data +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604 + IrReceiver.printIRResultShort(&Serial); + IrReceiver.printIRSendUsage(&Serial); +#else + IrReceiver.printIRResultMinimal(&Serial); +#endif +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604 + if (IrReceiver.decodedIRData.protocol == UNKNOWN || digitalRead(DEBUG_BUTTON_PIN) == LOW) { + // We have an unknown protocol, print more info + IrReceiver.printIRResultRawFormatted(&Serial, true); + } +#endif + + if (IrReceiver.decodedIRData.protocol == PULSE_DISTANCE || IrReceiver.decodedIRData.protocol == PULSE_WIDTH) { + for (uint_fast8_t i = 0; i < aArraySize; ++i) { + if (IrReceiver.decodedIRData.decodedRawDataArray[i] != *aRawDataArrayPointer) { + Serial.print(F("ERROR: Received data=0x")); +# if (__INT_WIDTH__ < 32) + Serial.print(IrReceiver.decodedIRData.decodedRawDataArray[i], HEX); +# else + PrintULL::print(&Serial, IrReceiver.decodedIRData.decodedRawDataArray[i], HEX); +# endif + Serial.print(F(" != sent data=0x")); + Serial.println(*aRawDataArrayPointer, HEX); + } + aRawDataArrayPointer++; + } + } + IrReceiver.resume(); + } else { + Serial.println(F("No data received")); + } + Serial.println(); +} +#endif + +/* + * Test callback function + * Has the same functionality as a check with available() + */ +void ReceiveCompleteCallbackHandler() { + sDataJustReceived = true; +} + +void checkReceive(uint16_t aSentAddress, uint16_t aSentCommand) { + // wait until signal has received + uint16_t tTimeoutCounter = 1000; // gives 10 seconds timeout + while (!sDataJustReceived) { + delay(10); + if (tTimeoutCounter == 0) { + Serial.println(F("Receive timeout happened")); + break; + } + tTimeoutCounter--; + } + sDataJustReceived = false; + + if (IrReceiver.decode()) { +// Print a short summary of received data +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604 + IrReceiver.printIRResultShort(&Serial); + IrReceiver.printIRSendUsage(&Serial); +#else + IrReceiver.printIRResultMinimal(&Serial); +#endif + + if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) { + Serial.println(F("Try to increase the \"RAW_BUFFER_LENGTH\" value of " STR(RAW_BUFFER_LENGTH) " in " __FILE__)); + // see also https://github.com/Arduino-IRremote/Arduino-IRremote#compile-options--macros-for-this-library + } + +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604 + if (IrReceiver.decodedIRData.protocol == UNKNOWN || digitalRead(DEBUG_BUTTON_PIN) == LOW) { + // We have an unknown protocol, print more info + IrReceiver.printIRResultRawFormatted(&Serial, true); + } +#endif + IrReceiver.resume(); // Early resume + + if (IrReceiver.decodedIRData.protocol == UNKNOWN) { + Serial.println(F("ERROR: Unknown protocol")); + } else { + /* + * Check address + */ + if (IrReceiver.decodedIRData.address != aSentAddress) { + Serial.print(F("ERROR: Received address=0x")); + Serial.print(IrReceiver.decodedIRData.address, HEX); + Serial.print(F(" != sent address=0x")); + Serial.println(aSentAddress, HEX); + } + /* + * Check command + */ + if (IrReceiver.decodedIRData.command != aSentCommand) { + Serial.print(F("ERROR: Received command=0x")); + Serial.print(IrReceiver.decodedIRData.command, HEX); + Serial.print(F(" != sent command=0x")); + Serial.println(aSentCommand, HEX); + } + } + + } else { + Serial.println(F("No data received")); + IrReceiver.resume(); + } + Serial.println(); +} + +/* + * Set up the data to be sent. + * For most protocols, the data is build up with a constant 8 (or 16 byte) address + * and a variable 8 bit command. + * There are exceptions like Sony and Denon, which have 5 bit address. + */ +uint16_t sAddress = 0xFFF1; +uint8_t sCommand = 0x76; +uint16_t s16BitCommand = 0x9876; +uint8_t sRepeats = 0; + +void loop() { + /* + * Print values + */ + Serial.println(); + Serial.print(F("address=0x")); + Serial.print(sAddress, HEX); + Serial.print(F(" command=0x")); + Serial.print(sCommand, HEX); + Serial.println(); + Serial.println(); + + Serial.print(F("Send NEC with 8 bit address")); + if (sRepeats > 0) { + Serial.print(F(" and complete NEC frames as repeats to force decoding as NEC2")); + } + Serial.println(); + Serial.flush(); + IrSender.sendNEC(sAddress & 0xFF, sCommand, 0); + checkReceive(sAddress & 0xFF, sCommand); + + /* + * Complete NEC frames as repeats to force decoding as NEC2 are tested here + */ + for (int8_t i = 0; i < sRepeats; i++) { + if (digitalRead(DEBUG_BUTTON_PIN) != LOW) { + // If debug is enabled, printing time (50 ms) is sufficient as delay + delayMicroseconds(NEC_REPEAT_DISTANCE - 20000); // 20000 is just a guess + } + IrSender.sendNEC(sAddress & 0xFF, sCommand, 0); + checkReceive(sAddress & 0xFF, sCommand); + } + + delay(DELAY_AFTER_SEND); // delay must be greater than 5 ms (RECORD_GAP_MICROS), otherwise the receiver sees it as one long signal + + Serial.println(F("Send NEC with 16 bit address")); + Serial.flush(); + IrSender.sendNEC(sAddress, sCommand, 0); + checkReceive(sAddress, sCommand); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send NEC2 with 16 bit address")); + Serial.flush(); + IrSender.sendNEC2(sAddress, sCommand, 0); + checkReceive(sAddress, sCommand); + delay(DELAY_AFTER_SEND); + +#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604. Code does not fit in program memory of ATtiny85 etc. + + if (sAddress == 0xFFF1) { +# if FLASHEND >= 0x7FFF && ((!defined(RAMEND) && !defined(RAMSIZE)) || (defined(RAMEND) && RAMEND > 0x6FF) || (defined(RAMSIZE) && RAMSIZE > 0x6FF)) // For 32k flash or more, like Uno. Code does not fit in program memory of ATtiny1604 etc. + IRRawDataType tRawData[4]; + + /* + * Send constant values only once in this demo + */ + Serial.println(F("Send NEC Pronto data with 8 bit address 0x80 and command 0x45 and no repeats")); + Serial.flush(); + // This is copied to stack/ram internally + IrSender.sendPronto(F("0000 006D 0022 0000 015E 00AB " /* Pronto header + start bit */ + "0017 0015 0017 0015 0017 0017 0015 0017 0017 0015 0017 0015 0017 0015 0017 003F " /* Lower address byte */ + "0017 003F 0017 003E 0017 003F 0015 003F 0017 003E 0017 003F 0017 003E 0017 0015 " /* Upper address byte (inverted at 8 bit mode) */ + "0017 003E 0017 0015 0017 003F 0017 0015 0017 0015 0017 0015 0017 003F 0017 0015 " /* command byte */ + "0019 0013 0019 003C 0017 0015 0017 003F 0017 003E 0017 003F 0017 0015 0017 003E " /* inverted command byte */ + "0017 0806"), 0); //stop bit, no repeat possible, because of missing repeat pattern + checkReceive(0x80, 0x45); + delay(DELAY_AFTER_SEND); + + Serial.println( + F("Send NEC sendRaw data with 8 bit address=0xFB04 and command 0x08 and exact timing (16 bit array format)")); + Serial.flush(); + const uint16_t irSignal[] = { 9000, 4500/*Start bit*/, 560, 560, 560, 560, 560, 1690, 560, + 560/*0010 0x4 of 16 bit address LSB first*/, 560, 560, 560, 560, 560, 560, 560, 560/*0000*/, 560, 1690, 560, 1690, + 560, 560, 560, 1690/*1101 0xB*/, 560, 1690, 560, 1690, 560, 1690, 560, 1690/*1111*/, 560, 560, 560, 560, 560, 560, + 560, 1690/*0001 0x08 of command LSB first*/, 560, 560, 560, 560, 560, 560, 560, 560/*0000 0x00*/, 560, 1690, 560, + 1690, 560, 1690, 560, 560/*1110 Inverted 8 of command*/, 560, 1690, 560, 1690, 560, 1690, 560, + 1690/*1111 inverted 0 of command*/, 560 /*stop bit*/}; // Using exact NEC timing + IrSender.sendRaw(irSignal, sizeof(irSignal) / sizeof(irSignal[0]), NEC_KHZ); // Note the approach used to automatically calculate the size of the array. + checkReceive(0xFB04 & 0xFF, 0x08); + delay(DELAY_AFTER_SEND); + + /* + * With sendNECRaw() you can send 32 bit codes directly, i.e. without parity etc. + */ + Serial.println(F("Send ONKYO with 16 bit address 0x0102 and 16 bit command 0x0304 with NECRaw(0x03040102)")); + Serial.flush(); + IrSender.sendNECRaw(0x03040102, 0); + checkReceive(0x0102, 0x304); + delay(DELAY_AFTER_SEND); + + /* + * With Send sendNECMSB() you can send your old 32 bit codes. + * To convert one into the other, you must reverse the byte positions and then reverse all positions of each byte. + * Use bitreverse32Bit(). + * Example: + * 0xCB340102 byte reverse -> 0x020134CB bit reverse-> 40802CD3 + */ + Serial.println(F("Send ONKYO with 16 bit address 0x0102 and command 0x34 with old 32 bit format MSB first (0x40802CD3)")); + Serial.flush(); + IrSender.sendNECMSB(0x40802CD3, 32, false); + checkReceive(0x0102, 0x34); + delay(DELAY_AFTER_SEND); + +# if defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO) + Serial.println(F("Send Panasonic 0xB, 0x10 as 48 bit PulseDistance using ProtocolConstants")); + Serial.flush(); +# if __INT_WIDTH__ < 32 + IrSender.sendPulseDistanceWidthFromPGMArray_P(&KaseikyoProtocolConstants, &tRawDataPGM[0], 48, NO_REPEATS); // Panasonic is a Kaseikyo variant + checkReceive(0x0B, 0x10); +# else + IrSender.sendPulseDistanceWidth_P(&KaseikyoProtocolConstants, 0xA010B02002, 48, NO_REPEATS); // Panasonic is a Kaseikyo variant + checkReceivedRawData(0xA010B02002); +# endif + delay(DELAY_AFTER_SEND); + + /* + * Send 2 Panasonic 48 bit codes as Pulse Distance data, once with LSB and once with MSB first + */ + Serial.println(F("Send Panasonic 0xB, 0x10 as 48 bit PulseDistance")); + Serial.println(F("-LSB first")); + Serial.flush(); +# if __INT_WIDTH__ < 32 + IrSender.sendPulseDistanceWidthFromPGMArray(38, 3450, 1700, 450, 1250, 450, 400, &tRawDataPGM[0], 48, PROTOCOL_IS_LSB_FIRST, + 0, NO_REPEATS); + checkReceive(0x0B, 0x10); +# else + IrSender.sendPulseDistanceWidth(38, 3450, 1700, 450, 1250, 450, 400, 0xA010B02002, 48, PROTOCOL_IS_LSB_FIRST, 0, + NO_REPEATS); + checkReceivedRawData(0xA010B02002); +# endif + delay(DELAY_AFTER_SEND); + + // The same with MSB first. Use bit reversed raw data of LSB first part + Serial.println(F("-MSB first")); +# if __INT_WIDTH__ < 32 + tRawData[0] = 0x40040D00; // MSB of tRawData[0] is sent first + tRawData[1] = 0x805; + IrSender.sendPulseDistanceWidthFromArray(38, 3450, 1700, 450, 1250, 450, 400, &tRawData[0], 48, PROTOCOL_IS_MSB_FIRST, 0, + NO_REPEATS); + checkReceive(0x0B, 0x10); +# else + IrSender.sendPulseDistanceWidth(38, 3450, 1700, 450, 1250, 450, 400, 0x40040D000805, 48, PROTOCOL_IS_MSB_FIRST, 0, + NO_REPEATS); + checkReceivedRawData(0x40040D000805); +# endif + + delay(DELAY_AFTER_SEND); +# endif // defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO) + +# if defined(DECODE_DISTANCE_WIDTH) +# if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER) + Serial.println(F("Send 52 bit PulseDistance 0x43D8613C and 0x3BC3B MSB first")); + Serial.flush(); +# if __INT_WIDTH__ < 32 + tRawData[0] = 0x43D8613C; // MSB of tRawData[0] is sent first + tRawData[1] = 0x3BC3B; + IrSender.sendPulseDistanceWidthFromArray(38, 8900, 4450, 550, 1700, 550, 600, &tRawData[0], 52, PROTOCOL_IS_MSB_FIRST, 0, + NO_REPEATS); + checkReceivedArray(tRawData, 2); +# else + IrSender.sendPulseDistanceWidth(38, 8900, 4450, 550, 1700, 550, 600, 0x43D8613CBC3B, 52, PROTOCOL_IS_MSB_FIRST, 0, NO_REPEATS); + checkReceivedRawData(0x43D8613CBC3B); +# endif + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send 52 bit PulseDistanceWidth 0x43D8613C and 0x3BC3B MSB first")); + Serial.flush(); + // Real PulseDistanceWidth (constant bit length) does not require a stop bit +# if __INT_WIDTH__ < 32 + IrSender.sendPulseDistanceWidthFromArray(38, 300, 600, 600, 300, 300, 600, &tRawData[0], 52, PROTOCOL_IS_MSB_FIRST, 0, 0); + checkReceivedArray(tRawData, 2); +# else + IrSender.sendPulseDistanceWidth(38, 300, 600, 600, 300, 300, 600, 0x123456789ABC, 52, PROTOCOL_IS_MSB_FIRST, 0, 0); + checkReceivedRawData(0x123456789ABC); +# endif + delay(DELAY_AFTER_SEND); + Serial.println(F("Send 32 bit PulseWidth 0x43D8613C MSB first")); + Serial.flush(); + // Real PulseDistanceWidth (constant bit length) does not require a stop bit + IrSender.sendPulseDistanceWidth(38, 1000, 500, 600, 300, 300, 300, 0x43D8613C, 32, PROTOCOL_IS_MSB_FIRST, 0, 0); + checkReceivedRawData(0x43D8613C); + delay(DELAY_AFTER_SEND); + +# else // defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER) + Serial.println(F("Send 72 bit PulseDistance 0x5A AFEDCBA9 87654321 LSB first")); + Serial.flush(); +# if __INT_WIDTH__ < 32 + tRawData[0] = 0x87654321; // LSB of tRawData[0] is sent first + tRawData[1] = 0xAFEDCBA9; + tRawData[2] = 0x5A; + IrSender.sendPulseDistanceWidthFromArray(38, 8900, 4450, 550, 1700, 550, 600, &tRawData[0], 72, PROTOCOL_IS_LSB_FIRST, 0, + NO_REPEATS); + checkReceivedArray(tRawData, 3); +# else + tRawData[0] = 0xAFEDCBA987654321; + tRawData[1] = 0x5A; // LSB of tRawData[0] is sent first + IrSender.sendPulseDistanceWidthFromArray(38, 8900, 4450, 550, 1700, 550, 600, &tRawData[0], 72, PROTOCOL_IS_LSB_FIRST, 0, + NO_REPEATS); + checkReceivedArray(tRawData, 2); +# endif + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send 52 bit PulseDistanceWidth 0xDCBA9 87654321 LSB first")); + Serial.flush(); + // Real PulseDistanceWidth (constant bit length) does theoretically not require a stop bit, but we know the stop bit from serial transmission +# if __INT_WIDTH__ < 32 + tRawData[1] = 0xDCBA9; + IrSender.sendPulseDistanceWidthFromArray(38, 300, 600, 300, 600, 600, 300, &tRawData[0], 52, PROTOCOL_IS_LSB_FIRST, 0, 0); + checkReceivedArray(tRawData, 2); +# else + IrSender.sendPulseDistanceWidth(38, 300, 600, 300, 600, 600, 300, 0xDCBA987654321, 52, PROTOCOL_IS_LSB_FIRST, 0, 0); + checkReceivedRawData(0xDCBA987654321); +# endif + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send 52 bit PulseDistanceWidth 0xDCBA9 87654321 LSB first with inverse timing and data")); + Serial.flush(); +# if __INT_WIDTH__ < 32 + tRawData[2] = ~tRawData[0]; + tRawData[3] = ~tRawData[1]; + IrSender.sendPulseDistanceWidthFromArray(38, 300, 600, 600, 300, 300, 600, &tRawData[2], 52, PROTOCOL_IS_LSB_FIRST, 0, 0); + checkReceivedArray(tRawData, 2); +# else + IrSender.sendPulseDistanceWidth(38, 300, 600, 600, 300, 300, 600, ~0xDCBA987654321, 52, PROTOCOL_IS_LSB_FIRST, 0, 0); + checkReceivedRawData(0xDCBA987654321); +# endif + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send 7 bit ASCII character with PulseDistanceWidth LSB first")); + Serial.flush(); + // Real PulseDistanceWidth (constant bit length) does theoretically not require a stop bit, but we know the stop bit from serial transmission + IrSender.sendPulseDistanceWidth(38, 6000, 500, 500, 1500, 1500, 500, sCommand, 7, PROTOCOL_IS_LSB_FIRST, 0, 0); + checkReceivedRawData(sCommand); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Sony12 as PulseWidth LSB first")); + Serial.flush(); + uint32_t tData = (uint32_t) sAddress << 7 | (sCommand & 0x7F); + IrSender.sendPulseDistanceWidth(38, 2400, 600, 1200, 600, 600, 600, tData, SIRCS_12_PROTOCOL, PROTOCOL_IS_LSB_FIRST, 0, 0); + checkReceive(sAddress & 0x1F, sCommand & 0x7F); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send 32 bit PulseWidth 0x87654321 LSB first")); + Serial.flush(); + IrSender.sendPulseDistanceWidth(38, 1000, 500, 600, 300, 300, 300, 0x87654321, 32, PROTOCOL_IS_LSB_FIRST, 0, 0); + checkReceivedRawData(0x87654321); + delay(DELAY_AFTER_SEND); + +# endif // defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER) +# endif // defined(DECODE_DISTANCE_WIDTH) +# endif // if FLASHEND >= 0x7FFF ... + +# if defined(DECODE_MAGIQUEST) + Serial.println(F("Send MagiQuest 0x6BCDFF00, 0x176 as 55 bit PulseDistanceWidth MSB first")); + Serial.flush(); +# if __INT_WIDTH__ < 32 + IRRawDataType tRawData1[2]; + tRawData1[0] = 0x01AF37FC; // We have 1 header (start) bit and 7 start bits and 31 address bits for MagiQuest, so 0x6BCDFF00 is shifted 2 left + tRawData1[1] = 0x017619; // We send only 23 bits here! 0x19 is the checksum + IrSender.sendPulseDistanceWidthFromArray(38, 287, 864, 576, 576, 287, 864, &tRawData1[0], 55, + PROTOCOL_IS_MSB_FIRST | SUPPRESS_STOP_BIT, 0, 0); +# else + // 0xD79BFE00 is 0x6BCDFF00 is shifted 1 left + IrSender.sendPulseDistanceWidth(38, 287, 864, 576, 576, 287, 864, 0xD79BFE017619, 55, PROTOCOL_IS_MSB_FIRST, 0, 0); +# endif + checkReceive(0xFF00, 0x176); + if (IrReceiver.decodedIRData.decodedRawData != 0x6BCDFF00) { + Serial.print(F("ERROR: Received address=0x")); +#if (__INT_WIDTH__ < 32) + Serial.print(IrReceiver.decodedIRData.decodedRawData, HEX); +#else + PrintULL::print(&Serial, IrReceiver.decodedIRData.decodedRawData, HEX); +#endif + Serial.println(F(" != sent address=0x6BCDFF00")); + Serial.println(); + } + delay(DELAY_AFTER_SEND); +# endif // defined(DECODE_MAGIQUEST) + + } +#endif // if FLASHEND >= 0x3FFF + + Serial.println(F("Send Onkyo (NEC with 16 bit command)")); + Serial.flush(); + IrSender.sendOnkyo(sAddress, (sCommand + 1) << 8 | sCommand, 0); + checkReceive(sAddress, (sCommand + 1) << 8 | sCommand); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Apple")); + Serial.flush(); + IrSender.sendApple(sAddress & 0xFF, sCommand, 0); + checkReceive(sAddress & 0xFF, sCommand); + delay(DELAY_AFTER_SEND); + +#if defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO) + Serial.println(F("Send Panasonic")); + Serial.flush(); + IrSender.sendPanasonic(sAddress & 0xFFF, sCommand, 0); + checkReceive(sAddress & 0xFFF, sCommand); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Kaseikyo with 0x4711 as Vendor ID")); + Serial.flush(); + IrSender.sendKaseikyo(sAddress & 0xFFF, sCommand, 0, 0x4711); + checkReceive(sAddress & 0xFFF, sCommand); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Kaseikyo_Denon variant")); + Serial.flush(); + IrSender.sendKaseikyo_Denon(sAddress & 0xFFF, sCommand, 0); + checkReceive(sAddress & 0xFFF, sCommand); + delay(DELAY_AFTER_SEND); +#endif + +#if defined(DECODE_DENON) + Serial.println(F("Send Denon")); + Serial.flush(); + IrSender.sendDenon(sAddress & 0x1F, sCommand, 0); + checkReceive(sAddress & 0x1F, sCommand); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Denon/Sharp variant")); + Serial.flush(); + IrSender.sendSharp(sAddress & 0x1F, sCommand, 0); + checkReceive(sAddress & 0x1F, sCommand); + delay(DELAY_AFTER_SEND); +#endif + +#if defined(DECODE_SONY) + Serial.println(F("Send Sony/SIRCS with 7 command and 5 address bits")); + Serial.flush(); + IrSender.sendSony(sAddress & 0x1F, sCommand, 0); // SIRCS_12_PROTOCOL is default + checkReceive(sAddress & 0x1F, sCommand & 0x7F); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Sony/SIRCS with 7 command and 8 address bits")); + Serial.flush(); + IrSender.sendSony(sAddress & 0xFF, sCommand, 0, SIRCS_15_PROTOCOL); + checkReceive(sAddress & 0xFF, sCommand & 0x7F); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Sony/SIRCS with 7 command and 13 address bits")); + Serial.flush(); + IrSender.sendSony(sAddress & 0x1FFF, sCommand, 0, SIRCS_20_PROTOCOL); + checkReceive(sAddress & 0x1FFF, sCommand & 0x7F); + delay(DELAY_AFTER_SEND); +#endif + +#if defined(DECODE_SAMSUNG) + Serial.println(F("Send Samsung 8 bit command and 8 bit address")); + Serial.flush(); + IrSender.sendSamsung(sAddress & 0xFF, sCommand, 0); + checkReceive(sAddress & 0xFF, sCommand); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Samsung 16 bit command and address")); + Serial.flush(); + IrSender.sendSamsung16BitAddressAndCommand(sAddress, s16BitCommand, 0); + checkReceive(sAddress, s16BitCommand); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Samsung48 16 bit command")); + Serial.flush(); + IrSender.sendSamsung48(sAddress, s16BitCommand, 0); + checkReceive(sAddress, s16BitCommand); + delay(DELAY_AFTER_SEND); +#endif + +#if defined(DECODE_RC5) + Serial.println(F("Send RC5")); + Serial.flush(); + IrSender.sendRC5(sAddress & 0x1F, sCommand & 0x3F, 0, true); // 5 address, 6 command bits + checkReceive(sAddress & 0x1F, sCommand & 0x3F); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send RC5X with 7.th MSB of command set")); + Serial.flush(); + IrSender.sendRC5(sAddress & 0x1F, (sCommand & 0x3F) + 0x40, 0, true); // 5 address, 7 command bits + checkReceive(sAddress & 0x1F, (sCommand & 0x3F) + 0x40); + delay(DELAY_AFTER_SEND); +#endif + +#if defined(DECODE_RC6) + Serial.println(F("Send RC6")); + Serial.flush(); + sLastSendToggleValue = sAddress & 0x01; // to modify toggling at each loop + + IrSender.sendRC6(sAddress & 0xFF, sCommand, 0, true); + checkReceive(sAddress & 0xFF, sCommand); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send RC6A with 14 bit 0x2711 as extra")); + Serial.flush(); + IrSender.sendRC6A(sAddress & 0xFF, sCommand, 0, 0x2711, true); + checkReceive(sAddress & 0xFF, sCommand); + delay(DELAY_AFTER_SEND); +#endif + +#if defined(DECODE_BEO) + Serial.println(F("Send Bang&Olufsen")); + Serial.flush(); + IrSender.sendBangOlufsen(sAddress & 0x0FF, sCommand, 0); +# if defined(ENABLE_BEO_WITHOUT_FRAME_GAP) + delay((RECORD_GAP_MICROS / 1000) + 1); + Serial.println(F("- ENABLE_BEO_WITHOUT_FRAME_GAP is enabled")); + Serial.println(F("- Now print raw data and try to decode the first 6 entries, which results in rawData 0x0")); + IrReceiver.printIRResultRawFormatted(&Serial, true); + uint8_t tOriginalRawlen = IrReceiver.decodedIRData.rawDataPtr->rawlen; + IrReceiver.decodedIRData.rawDataPtr->rawlen = 6; + /* + * decode first part / AGC part of frame + */ + IrReceiver.decode(); // sets IrReceiver.decodedIRData.rawlen to 6 + IrReceiver.printIRResultShort(&Serial); // -> Protocol=Bang&Olufsen Address=0x0 Command=0x0 Raw-Data=0x0 0 bits MSB first + + // Remove trailing 6 entries for second decode try + Serial.println(); + Serial.println( + F( + "- Remove trailing 6 entries, which is equivalent to define RECORD_GAP_MICROS < 15000, to enable successful B&O decode")); + IrReceiver.decodedIRData.rawlen = tOriginalRawlen - 6; + IrReceiver.decodedIRData.rawDataPtr->rawlen = tOriginalRawlen - 6; + for (uint_fast8_t i = 0; i < IrReceiver.decodedIRData.rawlen; ++i) { + IrReceiver.decodedIRData.rawDataPtr->rawbuf[i] = IrReceiver.decodedIRData.rawDataPtr->rawbuf[i + 6]; + } +# endif + checkReceive(sAddress & 0x0FF, sCommand); + delay(DELAY_AFTER_SEND); +#endif + +#if defined(DECODE_MAGIQUEST) + Serial.println(F("Send MagiQuest")); + Serial.flush(); + IrSender.sendMagiQuest(0x6BCD0000 | (uint32_t) sAddress, s16BitCommand); // we have 31 bit address + checkReceive(sAddress, s16BitCommand & 0x1FF); // we have 9 bit command + delay(DELAY_AFTER_SEND); +#endif + + /* + * Next example how to use the IrSender.write function + */ + IRData IRSendData; + // prepare data + IRSendData.address = sAddress; + IRSendData.command = sCommand; + IRSendData.flags = IRDATA_FLAGS_EMPTY; + + Serial.println(F("Send next protocols with IrSender.write")); + Serial.println(); + Serial.flush(); + +#if defined(DECODE_JVC) + IRSendData.protocol = JVC; // switch protocol + Serial.print(F("Send ")); + Serial.println(getProtocolString(IRSendData.protocol)); + Serial.flush(); + IrSender.write(&IRSendData, 0); + checkReceive(IRSendData.address & 0xFF, IRSendData.command); + delay(DELAY_AFTER_SEND); +#endif + +#if defined(DECODE_LG) || defined(DECODE_MAGIQUEST) + IRSendData.command = s16BitCommand; // LG support more than 8 bit command +#endif + +#if defined(DECODE_LG) + IRSendData.protocol = LG; + Serial.print(F("Send ")); + Serial.println(getProtocolString(IRSendData.protocol)); + Serial.flush(); + IrSender.write(&IRSendData, 0); + checkReceive(IRSendData.address & 0xFF, IRSendData.command); + delay(DELAY_AFTER_SEND); +#endif + +#if defined(DECODE_BOSEWAVE) + IRSendData.protocol = BOSEWAVE; + Serial.println(F("Send Bosewave with no address and 8 command bits")); + Serial.flush(); + IrSender.write(&IRSendData, 0); + checkReceive(0, IRSendData.command & 0xFF); + delay(DELAY_AFTER_SEND); +#endif + +#if defined(DECODE_FAST) + IRSendData.protocol = FAST; + Serial.print(F("Send ")); + Serial.println(getProtocolString(IRSendData.protocol)); + Serial.flush(); + IrSender.write(&IRSendData, 0); + checkReceive(0, IRSendData.command & 0xFF); + delay(DELAY_AFTER_SEND); +#endif + + /* + * LEGO is skipped, since it is difficult to receive because of its short marks and spaces + */ +// Serial.println(F("Send Lego with 2 channel and with 4 command bits")); +// Serial.flush(); +// IrSender.sendLegoPowerFunctions(sAddress, sCommand, LEGO_MODE_COMBO, true); +// checkReceive(sAddress, sCommand); // never has success for Lego protocol :-( +// delay(DELAY_AFTER_SEND); + /* + * Force buffer overflow + */ + Serial.println(F("Force buffer overflow by sending 450 marks and spaces")); + for (unsigned int i = 0; i < 225; ++i) { // 225 because we send 2 entries per loop + // 210 + 540 = 750 should be received as 5/10 or 4/11 if compensation by MARK_EXCESS_MICROS is optimal. + // 400 + 400 should be received as 8/8 and sometimes as 9/7 or 7/9 if compensation by MARK_EXCESS_MICROS is optimal. + IrSender.mark(210); // 8 pulses at 38 kHz + IrSender.space(540); // to fill up to 750 us + } + checkReceive(sAddress, sCommand); + delay(DELAY_AFTER_SEND); + + /* + * Increment values + * Also increment address just for demonstration, which normally makes no sense + */ + sAddress += 0x0101; + sCommand += 0x11; + s16BitCommand += 0x1111; + sRepeats++; + // clip repeats at 4 + if (sRepeats > 4) { + sRepeats = 4; + } + + /* + * Test stop and start of 50 us receiver timer + */ + Serial.println(F("Stop receiver")); + IrReceiver.stop(); + delay(DELAY_AFTER_LOOP); // additional delay at the end of each loop + Serial.println(F("Start receiver")); + IrReceiver.start(); // For ESP32 timerEnableReceiveInterrupt() is sufficient here, since timer is not reconfigured by another task +} + diff --git a/examples/UnitTest/UnitTest.log b/examples/UnitTest/UnitTest.log new file mode 100644 index 000000000..4063d890c --- /dev/null +++ b/examples/UnitTest/UnitTest.log @@ -0,0 +1,897 @@ +START ../src/UnitTest.cpp from Jun 22 2024 +Using library version 4.4.0 +Ready to receive IR signals of protocols: NEC/NEC2/Onkyo/Apple, Panasonic/Kaseikyo, Denon/Sharp, Sony, RC5, RC6, LG, JVC, Samsung, Bang & Olufsen, FAST, Bosewave, MagiQuest, Universal Pulse Distance Width, Hash at pin 2 +Use ReceiveCompleteCallback +Receive buffer length is 200 +Send IR signals at pin 3 +If you connect debug pin 5 to ground, raw data is always printed +Send signal mark duration for 38kHz is 8 us, pulse narrowing correction is 3000 ns, total period is 26 us +16000 us is the (minimum) gap, after which the start of a new IR packet is assumed +20 us are subtracted from all marks and added to all spaces for decoding + +address=0xFFF1 command=0x76 + +Send NEC with 8 bit address +Protocol=NEC Address=0xF1 Command=0x76 Raw-Data=0x89760EF1 32 bits LSB first Duration=67850 us +Send with: IrSender.sendNEC(0xF1, 0x76, ); +rawData[68]: + -1049200 + +8950,-4350 + + 650,-1600 + 650,- 500 + 600,- 500 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 650,- 500 + 600,-1600 + 650,-1600 + 650,-1600 + + 600,- 500 + 650,- 500 + 600,- 500 + 650,- 500 + + 600,- 500 + 650,-1600 + 650,-1600 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 600,-1650 + 600,- 500 + 650,- 500 + 600,-1600 + + 650,- 500 + 650,- 500 + 600,- 500 + 650,-1600 + + 650 +Duration=67850 us + +Send NEC with 16 bit address +Protocol=NEC Address=0xFFF1 Command=0x76 Raw-Data=0x8976FFF1 32 bits LSB first Duration=73350 us +Send with: IrSender.sendNEC(0xFFF1, 0x76, ); +rawData[68]: + -1060600 + +8950,-4400 + + 600,-1600 + 650,- 500 + 650,- 450 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 650,-1600 + 600,-1600 + 650,-1600 + 650,-1600 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 600,- 500 + 650,-1600 + 650,-1600 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 600,-1600 + 650,- 500 + 600,- 500 + 650,-1600 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,-1600 + + 650 +Duration=73350 us + +Send NEC2 with 16 bit address +Protocol=NEC Address=0xFFF1 Command=0x76 Raw-Data=0x8976FFF1 32 bits LSB first Duration=73400 us +Send with: IrSender.sendNEC(0xFFF1, 0x76, ); +rawData[68]: + -1061100 + +8950,-4400 + + 650,-1600 + 650,- 500 + 600,- 500 + 650,- 500 + + 650,-1600 + 600,-1600 + 650,-1600 + 650,-1600 + + 650,-1600 + 600,-1650 + 600,-1600 + 650,-1600 + + 650,-1600 + 650,-1600 + 600,-1600 + 650,-1600 + + 600,- 500 + 650,-1600 + 650,-1600 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 600,- 500 + + 650,-1600 + 650,- 500 + 600,- 500 + 650,-1600 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,-1600 + + 650 +Duration=73400 us + +Send Panasonic 0xB, 0x10 as 48 bit PulseDistance using ProtocolConstants +Protocol=Panasonic Address=0xB Command=0x10 Raw-Data=0xA01000B0 48 bits LSB first Duration=54150 us +Send with: IrSender.sendPanasonic(0xB, 0x10, ); +rawData[100]: + -1064750 + +3500,-1650 + + 500,- 350 + 500,-1250 + 450,- 400 + 500,- 350 + + 500,- 400 + 450,- 400 + 500,- 350 + 500,- 400 + + 450,- 400 + 500,- 350 + 500,- 400 + 450,- 400 + + 500,- 350 + 500,-1250 + 500,- 350 + 500,- 400 + + 450,- 400 + 500,- 350 + 500,- 400 + 450,- 400 + + 500,-1200 + 500,-1250 + 450,- 400 + 500,-1250 + + 450,- 400 + 500,- 350 + 500,- 400 + 450,- 400 + + 500,- 400 + 450,- 400 + 500,- 350 + 500,- 400 + + 500,- 400 + 450,- 400 + 500,- 350 + 500,- 350 + + 500,-1250 + 500,- 350 + 500,- 400 + 500,- 350 + + 500,- 400 + 450,- 400 + 500,- 350 + 500,- 400 + + 500,- 350 + 500,-1250 + 450,- 400 + 500,-1250 + + 450 +Duration=54150 us + +Send Panasonic 0xB, 0x10 as 48 bit PulseDistance +-LSB first +Protocol=Panasonic Address=0xB Command=0x10 Raw-Data=0xA01000B0 48 bits LSB first Duration=53200 us +Send with: IrSender.sendPanasonic(0xB, 0x10, ); +rawData[100]: + -1081400 + +3450,-1650 + + 550,- 300 + 500,-1200 + 500,- 350 + 500,- 350 + + 500,- 350 + 500,- 350 + 500,- 350 + 500,- 350 + + 500,- 350 + 500,- 350 + 550,- 300 + 500,- 350 + + 500,- 350 + 500,-1200 + 500,- 350 + 500,- 350 + + 500,- 350 + 500,- 350 + 500,- 350 + 500,- 350 + + 500,-1200 + 500,-1200 + 500,- 350 + 500,-1200 + + 500,- 350 + 500,- 350 + 500,- 350 + 500,- 350 + + 500,- 350 + 500,- 350 + 500,- 350 + 550,- 350 + + 500,- 350 + 500,- 350 + 500,- 350 + 500,- 300 + + 550,-1150 + 500,- 400 + 450,- 350 + 500,- 350 + + 500,- 350 + 500,- 350 + 500,- 350 + 500,- 350 + + 550,- 300 + 500,-1200 + 500,- 350 + 500,-1200 + + 500 +Duration=53200 us + +-MSB first +Protocol=Panasonic Address=0xB Command=0x10 Raw-Data=0xA01000B0 48 bits LSB first Duration=53200 us +Send with: IrSender.sendPanasonic(0xB, 0x10, ); +rawData[100]: + -1076300 + +3450,-1650 + + 500,- 350 + 550,-1150 + 500,- 400 + 500,- 300 + + 550,- 300 + 550,- 300 + 500,- 350 + 550,- 300 + + 500,- 350 + 500,- 350 + 500,- 350 + 500,- 350 + + 500,- 350 + 500,-1200 + 500,- 350 + 500,- 350 + + 500,- 350 + 500,- 350 + 500,- 350 + 500,- 350 + + 500,-1200 + 500,-1200 + 500,- 350 + 500,-1200 + + 500,- 350 + 500,- 350 + 500,- 350 + 500,- 350 + + 500,- 350 + 500,- 350 + 550,- 300 + 500,- 400 + + 500,- 350 + 500,- 350 + 500,- 350 + 500,- 300 + + 500,-1200 + 500,- 350 + 500,- 350 + 500,- 350 + + 500,- 350 + 500,- 350 + 500,- 350 + 500,- 350 + + 500,- 350 + 500,-1200 + 500,- 350 + 500,-1200 + + 500 +Duration=53200 us + +Send 72 bit PulseDistance 0x5A AFEDCBA9 87654321 LSB first +Protocol=PulseDistance Raw-Data=0x5A 72 bits LSB first Duration=138400 us +Send on a 8 bit platform with: + uint32_t tRawData[]={0x87654321, 0xAFEDCBA9, 0x5A}; + IrSender.sendPulseDistanceWidthFromArray(38, 8900, 4400, 600, 1650, 600, 550, &tRawData[0], 72, PROTOCOL_IS_LSB_FIRST, , ); +rawData[148]: + -1081200 + +8900,-4400 + + 600,-1600 + 600,- 550 + 600,- 550 + 600,- 550 + + 600,- 550 + 600,-1650 + 600,- 550 + 600,- 550 + + 600,-1650 + 600,-1650 + 600,- 550 + 600,- 550 + + 600,- 550 + 600,- 550 + 600,-1650 + 600,- 550 + + 600,-1650 + 600,- 550 + 600,-1650 + 600,- 550 + + 600,- 550 + 600,-1650 + 600,-1650 + 600,- 550 + + 600,-1650 + 600,-1650 + 600,-1650 + 600,- 550 + + 600,- 550 + 600,- 550 + 600,- 550 + 600,-1650 + + 650,-1600 + 600,- 550 + 600,- 550 + 600,-1650 + + 600,- 550 + 600,-1650 + 600,- 550 + 600,-1650 + + 600,-1650 + 600,-1650 + 600,- 550 + 600,-1650 + + 600,- 550 + 600,- 550 + 600,-1650 + 600,-1650 + + 550,-1700 + 600,- 550 + 600,-1650 + 600,-1650 + + 600,- 550 + 600,-1650 + 600,-1650 + 600,-1650 + + 550,-1650 + 600,-1650 + 600,-1650 + 600,-1650 + + 600,- 550 + 600,-1650 + 600,- 550 + 600,-1700 + + 550,- 550 + 600,-1650 + 600,- 550 + 600,-1650 + + 600,-1650 + 600,- 550 + 600,-1650 + 600,- 550 + + 600 +Duration=138400 us + +Send 52 bit PulseDistanceWidth 0xDCBA9 87654321 LSB first +Protocol=PulseDistance Raw-Data=0xDCBA9 52 bits LSB first Duration=48250 us +Send on a 8 bit platform with: + uint32_t tRawData[]={0x87654321, 0xDCBA9}; + IrSender.sendPulseDistanceWidthFromArray(38, 350, 550, 350, 550, 650, 250, &tRawData[0], 52, PROTOCOL_IS_LSB_FIRST, , ); +rawData[108]: + -1118250 + + 350,- 550 + + 400,- 500 + 700,- 200 + 650,- 250 + 700,- 200 + + 650,- 250 + 400,- 550 + 650,- 250 + 650,- 250 + + 350,- 550 + 350,- 550 + 650,- 250 + 650,- 250 + + 650,- 250 + 650,- 200 + 400,- 550 + 650,- 200 + + 400,- 550 + 650,- 200 + 400,- 550 + 650,- 200 + + 700,- 200 + 400,- 550 + 350,- 550 + 650,- 250 + + 350,- 550 + 350,- 550 + 350,- 550 + 650,- 250 + + 650,- 250 + 650,- 250 + 650,- 250 + 350,- 550 + + 400,- 550 + 650,- 250 + 650,- 200 + 400,- 500 + + 700,- 200 + 400,- 550 + 650,- 250 + 350,- 550 + + 350,- 550 + 350,- 550 + 650,- 250 + 400,- 500 + + 700,- 250 + 650,- 250 + 350,- 550 + 350,- 550 + + 350,- 550 + 650,- 250 + 350,- 550 + 350,- 550 + + 400 +Duration=48250 us + +Send 52 bit PulseDistanceWidth 0xDCBA9 87654321 LSB first with inverse timing and data +Protocol=PulseDistance Raw-Data=0xDCBA9 52 bits LSB first Duration=48500 us +Send on a 8 bit platform with: + uint32_t tRawData[]={0x87654321, 0xDCBA9}; + IrSender.sendPulseDistanceWidthFromArray(38, 350, 550, 350, 550, 650, 250, &tRawData[0], 52, PROTOCOL_IS_LSB_FIRST, , ); +rawData[108]: + -1097950 + + 350,- 550 + + 400,- 500 + 650,- 250 + 650,- 250 + 650,- 250 + + 650,- 250 + 400,- 500 + 700,- 200 + 700,- 250 + + 350,- 550 + 350,- 550 + 650,- 250 + 650,- 250 + + 650,- 250 + 650,- 250 + 350,- 550 + 650,- 250 + + 350,- 550 + 650,- 250 + 350,- 550 + 650,- 250 + + 650,- 250 + 400,- 550 + 350,- 500 + 650,- 250 + + 400,- 500 + 400,- 500 + 400,- 550 + 650,- 250 + + 650,- 200 + 700,- 200 + 650,- 250 + 400,- 550 + + 400,- 500 + 650,- 250 + 650,- 250 + 400,- 550 + + 600,- 250 + 400,- 500 + 650,- 250 + 400,- 550 + + 350,- 550 + 350,- 550 + 650,- 250 + 350,- 550 + + 650,- 250 + 650,- 250 + 350,- 550 + 350,- 550 + + 350,- 550 + 650,- 250 + 350,- 550 + 400,- 500 + + 650 +Duration=48500 us + +Send 7 bit ASCII character with PulseDistanceWidth LSB first +Protocol=PulseDistance Raw-Data=0x76 7 bits LSB first Duration=20950 us +Send on a 8 bit platform with: IrSender.sendPulseDistanceWidth(38, 6000, 450, 550, 1450, 1550, 450, 0x76, 7, PROTOCOL_IS_LSB_FIRST, , ); +rawData[18]: + -1095750 + +6000,- 450 + +1550,- 450 + 550,-1400 + 550,-1450 +1550,- 450 + + 550,-1450 + 550,-1450 + 550,-1450 + 550 +Duration=20950 us + +Send Sony12 as PulseWidth LSB first +Protocol=Sony Address=0x11 Command=0x76 Raw-Data=0x8F6 12 bits LSB first Duration=21050 us +Send with: IrSender.sendSony(0x11, 0x76, 2, 12); +rawData[26]: + -1038650 + +2500,- 500 + + 700,- 500 +1250,- 550 +1250,- 550 + 650,- 550 + +1250,- 550 +1250,- 550 +1250,- 550 +1250,- 550 + + 650,- 550 + 650,- 550 + 650,- 550 +1250 +Duration=21050 us + +Send 32 bit PulseWidth 0x87654321 LSB first +Protocol=PulseWidth Raw-Data=0x87654321 32 bits LSB first Duration=24650 us +Send on a 8 bit platform with: IrSender.sendPulseDistanceWidth(38, 1100, 450, 650, 250, 350, 250, 0x87654321, 32, PROTOCOL_IS_LSB_FIRST, , ); +rawData[66]: + -1038250 + +1100,- 450 + + 600,- 300 + 350,- 250 + 350,- 250 + 400,- 200 + + 400,- 250 + 600,- 300 + 350,- 250 + 350,- 250 + + 650,- 250 + 650,- 250 + 400,- 250 + 350,- 250 + + 350,- 250 + 400,- 200 + 650,- 250 + 400,- 250 + + 650,- 250 + 350,- 250 + 650,- 250 + 350,- 250 + + 350,- 250 + 650,- 250 + 700,- 200 + 400,- 250 + + 650,- 200 + 700,- 200 + 700,- 200 + 400,- 250 + + 350,- 250 + 350,- 250 + 400,- 200 + 700 +Duration=24650 us + +Send MagiQuest 0x6BCDFF00, 0x176 as 55 bit PulseDistanceWidth MSB first Duration=63850 us +Protocol=MagiQuest Address=0xFF00 Command=0x176 Raw-Data=0x6BCDFF00 56 bits MSB first +Send with: IrSender.sendMagiQuest(0x6BCDFF00, 0x176, ); +rawData[112]: + -1065600 + + 350,- 800 + 350,- 800 + 350,- 800 + 350,- 800 + + 350,- 800 + 350,- 800 + 350,- 800 + 350,- 800 + + 600,- 550 + 600,- 550 + 350,- 800 + 600,- 550 + + 350,- 800 + 600,- 550 + 600,- 550 + 600,- 550 + + 600,- 550 + 350,- 800 + 350,- 800 + 600,- 550 + + 600,- 550 + 350,- 800 + 600,- 550 + 600,- 550 + + 600,- 550 + 600,- 550 + 600,- 550 + 600,- 550 + + 600,- 550 + 600,- 550 + 600,- 550 + 300,- 850 + + 350,- 850 + 300,- 850 + 300,- 800 + 350,- 800 + + 350,- 800 + 350,- 800 + 350,- 800 + 600,- 550 + + 350,- 800 + 650,- 500 + 600,- 600 + 550,- 550 + + 300,- 850 + 600,- 550 + 600,- 550 + 350,- 800 + + 300,- 850 + 350,- 800 + 350,- 800 + 600,- 550 + + 600,- 550 + 350,- 800 + 350,- 800 + 600 +Duration=63850 us + +Send Onkyo (NEC with 16 bit command) +Protocol=Onkyo Address=0xFFF1 Command=0x7776 Raw-Data=0x7776FFF1 32 bits LSB first Duration=76750 us +Send with: IrSender.sendOnkyo(0xFFF1, 0x7776, ); +rawData[68]: + -1115300 + +8950,-4400 + + 650,-1600 + 650,- 500 + 600,- 500 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 650,-1600 + 600,-1650 + 600,-1600 + 650,-1600 + + 650,-1600 + 650,-1600 + 600,-1650 + 600,-1600 + + 650,- 500 + 600,-1600 + 650,-1600 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 600,- 550 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 600 +Duration=76750 us + +Send Apple +Protocol=Apple Address=0xF1 Command=0x76 Raw-Data=0xF17687EE 32 bits LSB first Duration=72250 us +Send with: IrSender.sendApple(0xF1, 0x76, ); +rawData[68]: + -1060250 + +8950,-4400 + + 600,- 500 + 650,-1600 + 650,-1600 + 650,-1600 + + 650,- 500 + 600,-1600 + 650,-1600 + 650,-1600 + + 600,-1650 + 600,-1600 + 650,-1600 + 600,- 500 + + 650,- 500 + 600,- 500 + 650,- 500 + 650,-1600 + + 600,- 500 + 650,-1600 + 650,-1600 + 600,- 500 + + 650,-1600 + 650,-1600 + 650,-1600 + 600,- 500 + + 650,-1600 + 650,- 500 + 600,- 500 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 600 +Duration=72250 us + +Send Panasonic +Protocol=Panasonic Address=0xFF1 Command=0x76 Raw-Data=0x9976FF10 48 bits LSB first Duration=64450 us +Send with: IrSender.sendPanasonic(0xFF1, 0x76, ); +rawData[100]: + -1059900 + +3500,-1650 + + 500,- 400 + 500,-1200 + 500,- 400 + 450,- 400 + + 500,- 350 + 500,- 400 + 500,- 350 + 500,- 350 + + 500,- 400 + 500,- 350 + 500,- 400 + 450,- 400 + + 500,- 350 + 500,-1250 + 450,- 400 + 500,- 350 + + 500,- 400 + 500,- 350 + 500,- 350 + 500,- 400 + + 500,-1200 + 500,- 400 + 450,- 400 + 500,- 350 + + 500,-1250 + 450,-1250 + 500,-1250 + 500,-1200 + + 500,-1200 + 500,-1250 + 500,-1200 + 500,-1250 + + 500,- 400 + 450,-1250 + 500,-1250 + 450,- 400 + + 500,-1250 + 450,-1250 + 500,-1250 + 450,- 400 + + 500,-1200 + 500,- 400 + 450,- 400 + 500,-1250 + + 450,-1250 + 500,- 350 + 500,- 400 + 450,-1250 + + 500 +Duration=64450 us + +Send Kaseikyo with 0x4711 as Vendor ID +Protocol=Kaseikyo Address=0xFF1 Command=0x76 Extra=0x4711 Raw-Data=0x9A76FF13 48 bits LSB first Duration=69550 us +Send with: IrSender.sendKaseikyo(0xFF1, 0x76, , 0x4711); +rawData[100]: + -1080100 + +3450,-1700 + + 500,-1200 + 500,- 400 + 450,- 400 + 500,- 350 + + 500,-1250 + 450,- 400 + 500,- 350 + 500,- 400 + + 450,-1250 + 500,-1250 + 450,-1250 + 500,- 400 + + 450,- 400 + 500,- 350 + 500,-1250 + 500,- 350 + + 500,-1250 + 450,-1250 + 500,- 400 + 450,- 400 + + 500,-1200 + 500,- 400 + 450,- 400 + 500,- 350 + + 500,-1250 + 450,-1250 + 500,-1250 + 450,-1250 + + 500,-1250 + 450,-1250 + 500,-1250 + 450,-1300 + + 450,- 400 + 500,-1200 + 500,-1250 + 450,- 400 + + 500,-1250 + 450,-1250 + 500,-1250 + 450,- 400 + + 500,- 350 + 500,-1250 + 500,- 350 + 500,-1250 + + 450,-1250 + 500,- 400 + 450,- 400 + 500,-1200 + + 500 +Duration=69550 us + +Send Kaseikyo_Denon variant +Protocol=Kaseikyo_Denon Address=0xFF1 Command=0x76 Raw-Data=0x9976FF10 48 bits LSB first Duration=67900 us +Send with: IrSender.sendKaseikyo_Denon(0xFF1, 0x76, ); +rawData[100]: + -1080850 + +3500,-1650 + + 500,- 400 + 500,- 350 + 500,-1250 + 450,- 400 + + 500,-1250 + 450,- 400 + 500,-1200 + 500,- 400 + + 500,- 350 + 500,-1200 + 500,- 400 + 450,- 400 + + 500,-1200 + 500,-1250 + 450,- 400 + 500,- 350 + + 500,- 400 + 450,- 400 + 500,- 350 + 500,- 400 + + 500,-1200 + 500,- 400 + 450,- 400 + 500,- 350 + + 500,-1250 + 500,-1200 + 500,-1250 + 500,-1250 + + 450,-1250 + 500,-1250 + 500,-1200 + 500,-1250 + + 500,- 350 + 500,-1250 + 500,-1250 + 450,- 400 + + 500,-1200 + 500,-1250 + 500,-1200 + 500,- 350 + + 500,-1250 + 500,- 350 + 500,- 350 + 500,-1250 + + 500,-1250 + 450,- 400 + 500,- 350 + 500,-1250 + + 500 +Duration=67900 us + +Send Denon +Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0xED1 15 bits LSB first Duration=23150 us +Send with: IrSender.sendDenon(0x11, 0x76, ); +rawData[32]: + -1078600 + + 300,-1750 + 300,- 750 + 300,- 750 + 300,- 750 + + 300,-1750 + 350,- 700 + 350,-1700 + 350,-1750 + + 300,- 750 + 300,-1750 + 350,-1700 + 350,-1750 + + 300,- 750 + 300,- 750 + 300,- 700 + 350 +Duration=23150 us + +Send Denon/Sharp variant +Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0x2ED1 15 bits LSB first Duration=24200 us +Send with: IrSender.sendDenon(0x11, 0x76, ); +rawData[32]: + -1020800 + + 300,-1750 + 350,- 700 + 350,- 700 + 300,- 750 + + 300,-1750 + 350,- 700 + 350,-1700 + 350,-1750 + + 350,- 700 + 300,-1750 + 350,-1750 + 300,-1750 + + 350,- 700 + 350,-1700 + 350,- 700 + 350 +Duration=24200 us + +Send Sony/SIRCS with 7 command and 5 address bits +Protocol=Sony Address=0x11 Command=0x76 Raw-Data=0x8F6 12 bits LSB first Duration=21050 us +Send with: IrSender.sendSony(0x11, 0x76, 2, 12); +rawData[26]: + -1023000 + +2450,- 550 + + 700,- 500 +1300,- 500 +1250,- 550 + 650,- 550 + +1250,- 550 +1250,- 550 +1250,- 550 +1250,- 550 + + 700,- 500 + 600,- 600 + 650,- 550 +1250 +Duration=21050 us + +Send Sony/SIRCS with 7 command and 8 address bits +Protocol=Sony Address=0xF1 Command=0x76 Raw-Data=0x78F6 15 bits LSB first Duration=26450 us +Send with: IrSender.sendSony(0xF1, 0x76, 2, 15); +rawData[32]: + -1038750 + +2450,- 550 + + 700,- 500 +1250,- 550 +1250,- 550 + 650,- 550 + +1250,- 550 +1250,- 550 +1250,- 550 +1250,- 550 + + 650,- 550 + 650,- 550 + 650,- 550 +1250,- 550 + +1250,- 550 +1250,- 550 +1250 +Duration=26450 us + +Send Sony/SIRCS with 7 command and 5 address bits +Protocol=Sony Address=0x11 Command=0x76 Raw-Data=0x8F6 12 bits LSB first Duration=21050 us +Send with: IrSender.sendSony(0x11, 0x76, 2, 12); +rawData[26]: + -1020900 + +2450,- 550 + + 650,- 550 +1250,- 550 +1250,- 550 + 650,- 550 + +1250,- 550 +1250,- 550 +1250,- 550 +1250,- 550 + + 650,- 550 + 650,- 550 + 650,- 550 +1250 +Duration=21050 us + +Send Sony/SIRCS with 7 command and 8 address bits +Protocol=Sony Address=0xF1 Command=0x76 Raw-Data=0x78F6 15 bits LSB first Duration=26450 us +Send with: IrSender.sendSony(0xF1, 0x76, 2, 15); +rawData[32]: + -1036650 + +2450,- 550 + + 650,- 550 +1250,- 550 +1250,- 550 + 650,- 550 + +1250,- 550 +1250,- 550 +1250,- 550 +1250,- 550 + + 650,- 550 + 650,- 550 + 650,- 550 +1250,- 550 + +1250,- 550 +1250,- 550 +1250 +Duration=26450 us + +Send Sony/SIRCS with 7 command and 13 address bits +Protocol=Sony Address=0x1FF1 Command=0x76 Raw-Data=0xFF8F6 20 bits LSB first Duration=35400 us +Send with: IrSender.sendSony(0x1FF1, 0x76, 2, 20); +rawData[42]: + -1040400 + +2400,- 550 + + 650,- 550 +1250,- 550 +1250,- 550 + 650,- 550 + +1250,- 550 +1250,- 550 +1250,- 550 +1250,- 550 + + 650,- 550 + 600,- 600 + 650,- 550 +1250,- 550 + +1250,- 550 +1250,- 550 +1250,- 550 +1250,- 550 + +1250,- 550 +1250,- 550 +1250,- 550 +1250 +Duration=35400 us + +Send Samsung 16 bit command and address +Protocol=Samsung Address=0xFFF1 Command=0x9876 Raw-Data=0x9876FFF1 32 bits LSB first Duration=69000 us +Send with: IrSender.sendSamsung(0xFFF1, 0x9876, ); +rawData[68]: + -1057350 + +4550,-4350 + + 650,-1600 + 650,- 500 + 600,- 500 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 600,-1650 + 600,-1600 + 650,-1600 + 650,-1600 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 600,- 500 + 650,-1600 + 650,-1600 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 600,- 500 + + 650,- 500 + 650,- 450 + 650,- 500 + 650,-1600 + + 650,-1600 + 600,- 500 + 650,- 500 + 650,-1600 + + 650 +Duration=69000 us + +Send Samsung48 16 bit command +Protocol=Samsung48 Address=0xFFF1 Command=0x9876 Raw-Data=0x6798 48 bits LSB first Duration=95900 us +Send with: IrSender.sendSamsung48(0xFFF1, 0x9876, ); +rawData[100]: + -1039600 + +4550,-4400 + + 600,-1600 + 650,- 500 + 650,- 450 + 650,- 500 + + 600,-1650 + 600,-1600 + 650,-1600 + 650,-1600 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 650,-1600 + 600,-1600 + 650,-1600 + 650,-1600 + + 650,- 500 + 600,-1600 + 650,-1600 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 600,-1600 + 650,- 500 + 650,- 500 + 600,-1600 + + 600,- 550 + 600,- 500 + 650,- 500 + 600,-1650 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,-1600 + + 650,-1600 + 650,- 500 + 600,- 500 + 650,-1600 + + 650,-1600 + 600,-1650 + 600,-1600 + 650,- 500 + + 600,- 500 + 650,-1600 + 650,-1600 + 650,- 450 + + 650 +Duration=95900 us + +Send RC5 +Protocol=RC5 Address=0x11 Command=0x36 Raw-Data=0x1476 13 bits MSB first Duration=23200 us +Send with: IrSender.sendRC5(0x11, 0x36, ); +rawData[20]: + -1078850 + + 950,- 850 + +1850,-1700 +1800,- 850 + 950,- 850 + 950,-1700 + + 950,- 850 + 950,- 800 +1850,-1700 + 950,- 850 + +1850 +Duration=23200 us + +Send RC5X with 7.th MSB of command set +Protocol=RC5 Address=0x11 Command=0x76 Toggle=1 Raw-Data=0xC76 13 bits MSB first Duration=23150 us +Send with: IrSender.sendRC5(0x11, 0x76, ); +rawData[20]: + -1037700 + +1850,-1700 + + 950,- 850 +1800,- 850 + 950,- 800 + 950,-1750 + + 950,- 800 + 950,- 850 +1800,-1750 + 950,- 800 + +1850 +Duration=23150 us + +Send RC6 +Protocol=RC6 Address=0xF1 Command=0x76 Raw-Data=0xF176 20 bits MSB first Duration=23300 us +Send with: IrSender.sendRC6(0xF1, 0x76, ); +rawData[36]: + -1035050 + +2700,- 800 + + 500,- 850 + 500,- 400 + 500,- 400 + 500,- 850 + +1400,- 350 + 500,- 400 + 500,- 400 + 500,- 850 + + 500,- 400 + 500,- 400 + 950,- 850 + 950,- 400 + + 500,- 400 + 500,- 850 + 950,- 400 + 500,- 850 + + 500 +Duration=23300 us + +Send RC6A with 14 bit 0x2711 as extra +Protocol=RC6A Address=0xF1 Command=0x76 Extra=0x2711 Toggle=1 Raw-Data=0xA711F176 35 bits MSB first Duration=36800 us +Send with: IrSender.sendRC6A(0xF1, 0x76, , 0x2711); +rawData[58]: + -1045150 + +2700,- 800 + + 500,- 400 + 500,- 450 + 450,- 900 +1350,-1300 + + 950,- 850 + 500,- 400 + 900,- 400 + 500,- 450 + + 450,- 850 + 500,- 400 + 500,- 400 + 950,- 850 + + 500,- 400 + 500,- 400 +1000,- 400 + 450,- 400 + + 500,- 450 + 500,- 400 + 450,- 850 + 500,- 400 + + 500,- 400 +1000,- 800 +1000,- 400 + 450,- 450 + + 450,- 850 + 950,- 400 + 500,- 850 + 500 +Duration=36800 us + +Send Bang&Olufsen +- ENABLE_BEO_WITHOUT_FRAME_GAP is enabled +- Now print raw data and try to decode the first 6 entries, which results in rawData 0x0 +rawData[36]: + -1033000 + + 250,-2850 + + 250,-2850 + 250,-15250 + 250,-2850 + 250,-9050 + + 250,-6000 + 200,-5950 + 250,-6000 + 200,-2900 + + 250,-5950 + 200,-6000 + 200,-9050 + 250,-2900 + + 250,-9000 + 250,-6000 + 250,-5950 + 250,-2850 + + 250 +Duration=105700 us +Protocol=Bang&Olufsen Address=0x0 Command=0x0 Raw-Data=0x0 0 bits MSB first + +- Remove trailing 6 entries, which is equivalent to define RECORD_GAP_MICROS < 15000, to enable successful B&O decode +Protocol=Bang&Olufsen Address=0xF1 Command=0x76 Raw-Data=0xF176 16 bits MSB first Duration=115000 us +Send with: IrSender.sendBang&Olufsen(0xF1, 0x76, ); +rawData[38]: + -15250 + + 250,-2850 + + 250,-9050 + 250,-6000 + 200,-5950 + 250,-6000 + + 200,-2900 + 250,-5950 + 200,-6000 + 200,-9050 + + 250,-2900 + 250,-9000 + 250,-6000 + 250,-5950 + + 250,-2850 + 250,-9100 + 200,-5950 + 250,-2900 + + 200,-12150 + 250 +Duration=115000 us + +Send MagiQuest +Protocol=MagiQuest Address=0xFFF1 Command=0x76 Raw-Data=0x6BCDFFF1 56 bits MSB first Duration=63900 us +Send with: IrSender.sendMagiQuest(0x6BCDFFF1, 0x76, ); +rawData[112]: + -1089250 + + 350,- 800 + 350,- 800 + 350,- 800 + 350,- 800 + + 350,- 800 + 350,- 800 + 350,- 800 + 350,- 850 + + 600,- 550 + 600,- 550 + 350,- 800 + 600,- 550 + + 350,- 750 + 650,- 500 + 650,- 500 + 650,- 500 + + 650,- 500 + 300,- 850 + 350,- 800 + 650,- 500 + + 650,- 500 + 350,- 800 + 650,- 500 + 650,- 500 + + 650,- 500 + 650,- 500 + 650,- 500 + 650,- 500 + + 650,- 500 + 650,- 500 + 650,- 500 + 650,- 500 + + 650,- 500 + 650,- 500 + 650,- 500 + 350,- 800 + + 350,- 800 + 350,- 800 + 650,- 550 + 300,- 800 + + 350,- 800 + 650,- 500 + 650,- 500 + 650,- 500 + + 350,- 800 + 650,- 500 + 650,- 500 + 350,- 850 + + 350,- 750 + 350,- 800 + 650,- 500 + 650,- 500 + + 350,- 800 + 650,- 500 + 650,- 500 + 650 +Duration=63900 us + +Send next protocols with IrSender.write + +Send JVC +Protocol=JVC Address=0xF1 Command=0x76 Raw-Data=0x76F1 16 bits LSB first Duration=40400 us +Send with: IrSender.sendJVC(0xF1, 0x76, ); +rawData[36]: + -1087300 + +8400,-4100 + + 600,-1500 + 600,- 450 + 600,- 450 + 600,- 450 + + 600,-1500 + 600,-1500 + 600,-1500 + 600,-1500 + + 600,- 450 + 600,-1500 + 600,-1500 + 600,- 450 + + 600,-1500 + 600,-1500 + 600,-1500 + 600,- 450 + + 600 +Duration=40400 us + +Send LG +Protocol=LG Address=0xF1 Command=0x9876 Raw-Data=0xF19876E 28 bits MSB first Duration=59450 us +Send with: IrSender.sendLG(0xF1, 0x9876, ); +rawData[60]: + -1041500 + +9000,-4150 + + 500,-1550 + 550,-1500 + 550,-1550 + 550,-1500 + + 550,- 500 + 550,- 500 + 550,- 500 + 550,-1550 + + 550,-1500 + 550,- 500 + 550,- 500 + 550,-1550 + + 550,-1500 + 550,- 500 + 550,- 500 + 550,- 500 + + 550,- 500 + 600,-1500 + 550,-1500 + 550,-1550 + + 550,- 500 + 550,-1500 + 550,-1550 + 550,- 500 + + 550,-1500 + 550,-1550 + 500,-1550 + 550,- 500 + + 550 +Duration=59450 us + +Send Bosewave with no address and 8 command bits +Protocol=BoseWave Address=0x0 Command=0x76 Raw-Data=0x8976 16 bits LSB first Duration=26800 us +Send with: IrSender.sendBoseWave(0x0, 0x76, ); +rawData[36]: + -1058350 + +1050,-1450 + + 550,- 400 + 600,-1400 + 550,-1400 + 600,- 400 + + 600,-1400 + 550,-1400 + 600,-1400 + 600,- 400 + + 550,-1400 + 600,- 400 + 600,- 400 + 600,-1400 + + 550,- 400 + 600,- 400 + 600,- 400 + 600,-1400 + + 550 +Duration=26800 us + +Send FAST +Protocol=FAST Address=0x0 Command=0x76 Raw-Data=0x8976 16 bits LSB first Duration=28900 us +Send with: IrSender.sendFAST(0x0, 0x76, ); +rawData[36]: + -1042350 + +2150,-1000 + + 550,- 500 + 600,-1500 + 550,-1550 + 550,- 500 + + 550,-1550 + 550,-1550 + 550,-1550 + 550,- 500 + + 550,-1550 + 550,- 500 + 550,- 500 + 600,-1500 + + 550,- 500 + 550,- 500 + 600,- 450 + 550,-1550 + + 550 +Duration=28900 us + +Force buffer overflow by sending 450 marks and spaces +Overflow +Try to increase the "RAW_BUFFER_LENGTH" value of 400 in ../src/UnitTest.cpp +rawData[400]: + -1041550 + + 300,- 500 + + 250,- 500 + 250,- 550 + 200,- 500 + 250,- 550 + + 200,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 550 + 200,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250,- 500 + 250,- 500 + + 250,- 500 + 250,- 500 + 250 +Duration=149550 us +ERROR: Unknown protocol + +Stop receiver +Start receiver + +address=0xF2 command=0x87 + +Send NEC with 8 bit address and complete NEC frames as repeats to force decoding as NEC2 +Protocol=NEC Address=0xF2 Command=0x87 Raw-Data=0x78870DF2 32 bits LSB first Duration=67850 us +Send with: IrSender.sendNEC(0xF2, 0x87, ); +rawData[68]: + -1028850 + +8900,-4400 + + 650,- 500 + 650,-1600 + 600,- 500 + 600,- 550 + + 600,-1650 + 600,-1600 + 650,-1600 + 650,-1600 + + 650,-1600 + 650,- 450 + 650,-1600 + 650,-1600 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,- 500 + + 650,-1600 + 650,-1600 + 650,-1600 + 650,- 450 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,-1600 + + 650,- 500 + 650,- 500 + 600,- 500 + 650,-1600 + + 650,-1600 + 650,-1600 + 600,-1650 + 600,- 500 + + 650 +Duration=67850 us + +Protocol=NEC2 Address=0xF2 Command=0x87 Repeat gap=67950us Raw-Data=0x78870DF2 32 bits LSB first Duration=67900 us +Send with: IrSender.sendNEC2(0xF2, 0x87, ); +rawData[68]: + -67950 + +8950,-4400 + + 600,- 550 + 600,-1650 + 600,- 500 + 600,- 550 + + 600,-1600 + 600,-1650 + 600,-1650 + 600,-1650 + + 600,-1650 + 600,- 550 + 600,-1600 + 600,-1650 + + 600,- 550 + 600,- 500 + 600,- 550 + 600,- 500 + + 600,-1650 + 600,-1650 + 600,-1650 + 600,- 500 + + 600,- 550 + 600,- 500 + 600,- 550 + 600,-1650 + + 600,- 500 + 600,- 550 + 600,- 500 + 600,-1650 + + 600,-1650 + 650,-1600 + 650,-1600 + 600,- 500 + + 650 +Duration=67900 us + +Send NEC with 16 bit address +Protocol=NEC Address=0xF2 Command=0x87 Raw-Data=0x78870DF2 32 bits LSB first Duration=67800 us +Send with: IrSender.sendNEC(0xF2, 0x87, ); +rawData[68]: + -1062200 + +8950,-4400 + + 600,- 500 + 650,-1600 + 650,- 450 + 650,- 500 + + 650,-1600 + 600,-1600 + 650,-1600 + 650,-1600 + + 650,-1600 + 600,- 500 + 650,-1600 + 650,-1600 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,- 500 + + 650,-1600 + 650,-1600 + 600,-1650 + 600,- 500 + + 650,- 450 + 650,- 500 + 650,- 500 + 600,-1600 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,-1600 + + 650,-1600 + 650,-1600 + 600,-1650 + 600,- 500 + + 650 +Duration=67800 us + +Send NEC2 with 16 bit address +Protocol=NEC Address=0xF2 Command=0x87 Raw-Data=0x78870DF2 32 bits LSB first Duration=67850 us +Send with: IrSender.sendNEC(0xF2, 0x87, ); +rawData[68]: + -1060650 + +8950,-4400 + + 650,- 500 + 600,-1650 + 600,- 500 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 600,-1650 + 600,- 500 + 650,-1600 + 600,-1600 + + 650,- 500 + 650,- 500 + 600,- 500 + 650,- 500 + + 600,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 600,- 500 + 650,- 500 + 600,- 500 + 650,-1600 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,-1600 + + 650,-1600 + 650,-1600 + 650,-1600 + 600,- 500 + + 650 +Duration=67850 us + +Send Onkyo (NEC with 16 bit command) +Protocol=Onkyo Address=0xF2 Command=0x8887 Raw-Data=0x888700F2 32 bits LSB first Duration=62250 us +Send with: IrSender.sendOnkyo(0xF2, 0x8887, ); +rawData[68]: + -1061250 + +8950,-4400 + + 650,- 500 + 600,-1600 + 650,- 500 + 650,- 500 + + 600,-1600 + 650,-1600 + 600,-1650 + 600,-1600 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,- 500 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,- 500 + + 650,-1600 + 650,-1600 + 650,-1600 + 650,- 450 + + 650,- 500 + 600,- 500 + 650,- 500 + 650,-1600 + + 600,- 500 + 650,- 500 + 650,- 450 + 650,-1600 + + 650,- 500 + 650,- 450 + 650,- 500 + 650,-1600 + + 600 +Duration=62250 us + +Send Apple +Protocol=Apple Address=0xF2 Command=0x87 Raw-Data=0xF28787EE 32 bits LSB first Duration=71150 us +Send with: IrSender.sendApple(0xF2, 0x87, ); +rawData[68]: + -1059700 + +8950,-4400 + + 600,- 500 + 650,-1600 + 650,-1600 + 650,-1550 + + 650,- 500 + 650,-1600 + 650,-1600 + 600,-1600 + + 650,-1600 + 650,-1600 + 600,-1600 + 650,- 500 + + 600,- 500 + 650,- 500 + 600,- 500 + 650,-1600 + + 650,-1600 + 650,-1600 + 600,-1600 + 650,- 500 + + 650,- 500 + 600,- 500 + 650,- 500 + 600,-1650 + + 600,- 500 + 650,-1600 + 650,- 500 + 600,- 500 + + 650,-1600 + 650,-1600 + 600,-1600 + 650,-1600 + + 650 +Duration=71150 us + +Send Panasonic +Protocol=Panasonic Address=0xF2 Command=0x87 Raw-Data=0xA8870F20 48 bits LSB first Duration=59250 us +Send with: IrSender.sendPanasonic(0xF2, 0x87, ); +rawData[100]: + -1059850 + +3500,-1650 + + 500,- 350 + 500,-1250 + 500,- 350 + 500,- 400 + + 450,- 400 + 500,- 350 + 500,- 400 + 450,- 400 + + 500,- 350 + 500,- 400 + 450,- 400 + 500,- 350 + + 500,- 400 + 500,-1200 + 500,- 400 + 450,- 400 + + 500,- 350 + 500,- 400 + 450,- 400 + 500,- 350 + + 500,- 400 + 500,-1200 + 500,- 400 + 450,- 400 + + 500,-1200 + 500,-1250 + 450,-1250 + 500,-1250 + + 450,- 400 + 500,- 350 + 500,- 400 + 450,- 400 + + 500,-1250 + 450,-1250 + 500,-1250 + 450,- 400 + + 500,- 350 + 500,- 400 + 500,- 350 + 500,-1250 + + 450,- 400 + 500,- 350 + 500,- 400 + 450,-1250 + + 500,- 350 + 500,-1250 + 500,- 350 + 500,-1200 + + 500 +Duration=59250 us + +Send Kaseikyo with 0x4711 as Vendor ID +Protocol=Kaseikyo Address=0xF2 Command=0x87 Extra=0x4711 Raw-Data=0xAB870F23 48 bits LSB first Duration=66200 us +Send with: IrSender.sendKaseikyo(0xF2, 0x87, , 0x4711); +rawData[100]: + -1079850 + +3450,-1700 + + 500,-1200 + 500,- 400 + 450,- 400 + 500,- 400 + + 450,-1250 + 500,- 350 + 500,- 400 + 500,- 350 + + 500,-1250 + 450,-1250 + 500,-1250 + 450,- 400 + + 500,- 350 + 500,- 400 + 450,-1250 + 500,- 350 + + 500,-1250 + 500,-1250 + 450,- 400 + 500,- 350 + + 500,- 400 + 450,-1250 + 500,- 400 + 450,- 400 + + 500,-1200 + 500,-1250 + 450,-1250 + 500,-1250 + + 450,- 400 + 500,- 350 + 500,- 400 + 500,- 400 + + 450,-1250 + 500,-1250 + 450,-1250 + 500,- 400 + + 450,- 400 + 500,- 350 + 500,- 400 + 450,-1250 + + 500,-1250 + 450,-1250 + 500,- 400 + 450,-1250 + + 500,- 400 + 450,-1250 + 500,- 350 + 500,-1250 + + 500 +Duration=66200 us + +Send Kaseikyo_Denon variant +Protocol=Kaseikyo_Denon Address=0xF2 Command=0x87 Raw-Data=0xA8870F20 48 bits LSB first Duration=62750 us +Send with: IrSender.sendKaseikyo_Denon(0xF2, 0x87, ); +rawData[100]: + -1080600 + +3500,-1650 + + 500,- 350 + 500,- 400 + 500,-1200 + 500,- 400 + + 450,-1250 + 500,- 350 + 500,-1250 + 500,- 350 + + 500,- 400 + 500,-1200 + 500,- 400 + 450,- 400 + + 500,-1250 + 450,-1250 + 500,- 350 + 500,- 400 + + 500,- 350 + 500,- 350 + 500,- 400 + 500,- 350 + + 500,- 400 + 450,-1250 + 500,- 350 + 500,- 400 + + 450,-1250 + 500,-1250 + 450,-1250 + 500,-1250 + + 450,- 400 + 500,- 350 + 500,- 400 + 500,- 400 + + 450,-1250 + 500,-1250 + 450,-1250 + 500,- 400 + + 450,- 400 + 500,- 350 + 500,- 400 + 450,-1250 + + 500,- 400 + 450,- 400 + 500,- 350 + 500,-1250 + + 450,- 400 + 500,-1250 + 450,- 400 + 500,-1200 + + 500 +Duration=62750 us + +Send Denon +Protocol=Denon Address=0x12 Command=0x87 Raw-Data=0x10F2 15 bits LSB first Duration=22150 us +Send with: IrSender.sendDenon(0x12, 0x87, ); +rawData[32]: + -1078350 + + 350,- 700 + 300,-1750 + 350,- 700 + 350,- 700 + + 350,-1700 + 350,-1750 + 300,-1750 + 350,-1750 + + 300,- 750 + 300,- 700 + 350,- 700 + 350,- 700 + + 300,-1750 + 350,- 700 + 350,- 700 + 350 +Duration=22150 us + +Send Denon/Sharp variant +Protocol=Denon Address=0x12 Command=0x87 Raw-Data=0x30F2 15 bits LSB first Duration=23200 us +Send with: IrSender.sendDenon(0x12, 0x87, ); +rawData[32]: + -1020850 + + 350,- 700 + 350,-1750 + 300,- 700 + 350,- 700 + + 350,-1750 + 300,-1750 + 350,-1700 + 350,-1750 + + 300,- 750 + 300,- 750 + 300,- 700 + 350,- 700 + + 350,-1750 + 300,-1750 + 350,- 700 + 350 +Duration=23200 us + +Send Sony/SIRCS with 7 command and 5 address bits +Protocol=Sony Address=0x12 Command=0x7 Raw-Data=0x907 12 bits LSB first Duration=19850 us +Send with: IrSender.sendSony(0x12, 0x7, 2, 12); +rawData[26]: + -1022950 + +2450,- 550 + +1250,- 550 +1250,- 550 +1250,- 550 + 650,- 550 + + 650,- 550 + 650,- 550 + 650,- 550 + 650,- 550 + +1250,- 550 + 650,- 550 + 650,- 550 +1250 +Duration=19850 us + + + diff --git a/examples/UnitTest/UnitTest_64bit.log b/examples/UnitTest/UnitTest_64bit.log new file mode 100644 index 000000000..7def9abbc --- /dev/null +++ b/examples/UnitTest/UnitTest_64bit.log @@ -0,0 +1,825 @@ +START UnitTest.cpp from Feb 24 2023 +Using library version 4.1.0 +Ready to receive IR signals of protocols: NEC/NEC2/Onkyo/Apple, Panasonic/Kaseikyo, Denon/Sharp, Sony, RC5, RC6, LG, JVC, Samsung, Bang & Olufsen, FAST, Bosewave , MagiQuest, Universal Pulse Distance Width, Hash at pin 14 +Send IR signals at pin 12 +If you connect debug pin 13 to ground, raw data is always printed +Send signal mark duration for 38kHz is 8 us, pulse narrowing correction is 600 ns, total period is 26 us +16000 us is the (minimum) gap, after which the start of a new IR packet is assumed +100 us are subtracted from all marks and added to all spaces for decoding + +address=0xFFF1 command=0x76 + +Send NEC with 8 bit address +Protocol=NEC Address=0xF1 Command=0x76 Raw-Data=0x89760EF1 32 bits LSB first +Send with: IrSender.sendNEC(0xF1, 0x76, ); +rawData[68]: + -1050650 + +9050,-4450 + + 650,-1600 + 700,- 450 + 650,- 500 + 650,- 450 + + 700,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 700,- 450 + 650,-1600 + 650,-1650 + 650,-1600 + + 650,- 450 + 700,- 450 + 650,- 500 + 650,- 450 + + 700,- 450 + 650,-1600 + 650,-1600 + 700,- 450 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,- 450 + + 700,-1600 + 650,- 450 + 700,- 450 + 650,-1600 + + 650,- 500 + 650,- 450 + 700,- 450 + 650,-1600 + + 700 +Sum: 68500 + +Send NEC with 16 bit address +Protocol=NEC Address=0xFFF1 Command=0x76 Raw-Data=0x8976FFF1 32 bits LSB first +Send with: IrSender.sendNEC(0xFFF1, 0x76, ); +rawData[68]: + -1060200 + +9050,-4400 + + 650,-1600 + 650,- 500 + 650,- 450 + 700,- 450 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,-1600 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,-1600 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,-1600 + + 650,- 500 + 650,-1600 + 650,-1600 + 650,- 500 + + 650,-1600 + 650,-1600 + 650,-1650 + 650,- 450 + + 650,-1600 + 700,- 450 + 650,- 500 + 650,-1600 + + 650,- 500 + 650,- 450 + 650,- 500 + 650,-1600 + + 650 +Sum: 74050 + +Send NEC2 with 16 bit address +Protocol=NEC Address=0xFFF1 Command=0x76 Raw-Data=0x8976FFF1 32 bits LSB first +Send with: IrSender.sendNEC(0xFFF1, 0x76, ); +rawData[68]: + -1060450 + +9050,-4450 + + 650,-1600 + 650,- 500 + 650,- 450 + 650,- 500 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,-1600 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,-1600 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,-1600 + + 650,- 500 + 650,-1600 + 650,-1600 + 650,- 500 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,- 500 + + 650,-1600 + 650,- 500 + 650,- 500 + 650,-1600 + + 650,- 450 + 700,- 450 + 650,- 500 + 650,-1600 + + 650 +Sum: 74150 + +Send NEC Pronto data with 8 bit address 0x80 and command 0x45 and no repeats +Protocol=NEC Address=0x80 Command=0x45 Raw-Data=0xBA457F80 32 bits LSB first +Send with: IrSender.sendNEC(0x80, 0x45, ); +rawData[68]: + -1065600 + +9200,-4400 + + 700,- 450 + 700,- 450 + 700,- 500 + 650,- 500 + + 700,- 500 + 650,- 500 + 650,- 550 + 700,-1550 + + 700,-1550 + 700,-1550 + 700,-1550 + 650,-1550 + + 700,-1550 + 700,-1550 + 700,-1550 + 700,- 450 + + 700,-1550 + 650,- 500 + 650,-1600 + 700,- 450 + + 700,- 450 + 700,- 450 + 700,-1550 + 700,- 450 + + 750,- 450 + 700,-1500 + 700,- 450 + 700,-1550 + + 700,-1550 + 700,-1550 + 700,- 450 + 700,-1550 + + 700 +Sum: 68800 + +Send NEC sendRaw data with 8 bit address=0xFB04 and command 0x08 and exact timing (16 bit array format) +Protocol=NEC Address=0x4 Command=0x8 Raw-Data=0xF708FB04 32 bits LSB first +Send with: IrSender.sendNEC(0x4, 0x8, ); +rawData[68]: + -1066550 + +9150,-4400 + + 650,- 500 + 650,- 450 + 700,-1600 + 650,- 450 + + 700,- 450 + 650,- 500 + 650,- 450 + 650,- 500 + + 650,-1600 + 700,-1600 + 650,- 450 + 700,-1600 + + 650,-1600 + 700,-1600 + 650,-1600 + 650,-1650 + + 650,- 450 + 700,- 450 + 650,- 500 + 650,-1600 + + 650,- 500 + 650,- 450 + 700,- 450 + 650,- 500 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,- 500 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,-1600 + + 700 +Sum: 68700 + +Send ONKYO with 16 bit address 0x0102 and 16 bit command 0x0304 with NECRaw(0x03040102) +Protocol=Onkyo Address=0x102 Command=0x304 Raw-Data=0x3040102 32 bits LSB first +Send with: IrSender.sendOnkyo(0x102, 0x304, ); +rawData[68]: + -1064800 + +9050,-4450 + + 650,- 450 + 650,-1600 + 700,- 450 + 650,- 500 + + 650,- 450 + 650,- 500 + 650,- 500 + 650,- 450 + + 650,-1600 + 700,- 450 + 700,- 450 + 650,- 500 + + 650,- 450 + 650,- 500 + 650,- 450 + 700,- 450 + + 650,- 500 + 650,- 450 + 700,-1600 + 650,- 450 + + 650,- 500 + 650,- 450 + 650,- 500 + 650,- 500 + + 650,-1600 + 650,-1600 + 650,- 500 + 650,- 450 + + 650,- 550 + 650,- 500 + 600,- 500 + 650,- 500 + + 650 +Sum: 56050 + +Send ONKYO with 16 bit address 0x0102 and command 0x34 with old 32 bit format MSB first (0x40802CD3) +Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 32 bits LSB first +Send with: IrSender.sendNEC(0x102, 0x34, ); +rawData[68]: + -1066900 + +9050,-4400 + + 650,- 500 + 650,-1600 + 650,- 500 + 650,- 450 + + 650,- 500 + 650,- 450 + 700,- 450 + 650,- 500 + + 650,-1600 + 650,- 500 + 650,- 450 + 650,- 550 + + 650,- 450 + 700,- 450 + 650,- 500 + 650,- 450 + + 650,- 500 + 650,- 450 + 700,-1600 + 650,- 450 + + 700,-1600 + 650,-1600 + 650,- 500 + 650,- 450 + + 650,-1600 + 700,-1600 + 600,- 500 + 650,-1650 + + 650,- 450 + 700,- 450 + 650,-1600 + 650,-1600 + + 700 +Sum: 61700 + +Send Panasonic 0xB, 0x10 as 48 bit generic PulseDistance using ProtocolConstants +Protocol=Panasonic Address=0x10B Command=0xA0 Raw-Data=0xA010B02002 48 bits LSB first +Send with: IrSender.sendPanasonic(0x10B, 0xA0, ); +rawData[100]: + -1064700 + +3550,-1650 + + 550,- 350 + 500,-1250 + 500,- 400 + 500,- 350 + + 500,- 400 + 500,- 350 + 500,- 400 + 500,- 350 + + 500,- 400 + 500,- 350 + 500,- 400 + 500,- 350 + + 500,- 400 + 450,-1300 + 500,- 350 + 500,- 400 + + 500,- 350 + 500,- 400 + 500,- 350 + 500,- 400 + + 500,-1250 + 500,-1250 + 500,- 350 + 500,-1250 + + 500,- 400 + 500,- 350 + 500,- 400 + 500,- 350 + + 500,-1250 + 500,- 400 + 500,- 350 + 500,- 400 + + 500,- 350 + 550,- 350 + 500,- 350 + 550,- 350 + + 500,- 350 + 500,-1250 + 500,- 400 + 500,-1250 + + 500,- 350 + 550,- 350 + 500,- 350 + 550,- 350 + + 500,- 350 + 550,- 350 + 500,- 400 + 500,- 350 + + 500 +Sum: 54750 + +Send Panasonic 0xB, 0x10 as generic 48 bit PulseDistance + LSB first +Protocol=Panasonic Address=0x10B Command=0xA0 Raw-Data=0xA010B02002 48 bits LSB first +Send with: IrSender.sendPanasonic(0x10B, 0xA0, ); +rawData[100]: + -1082450 + +3550,-1600 + + 550,- 350 + 550,-1150 + 550,- 300 + 600,- 300 + + 550,- 300 + 550,- 300 + 600,- 300 + 550,- 300 + + 550,- 300 + 550,- 350 + 550,- 300 + 550,- 350 + + 550,- 300 + 550,-1200 + 550,- 300 + 550,- 300 + + 550,- 350 + 550,- 300 + 550,- 300 + 550,- 350 + + 550,-1150 + 550,-1200 + 550,- 300 + 550,-1200 + + 550,- 300 + 550,- 300 + 550,- 300 + 600,- 300 + + 550,-1150 + 600,- 300 + 550,- 300 + 550,- 300 + + 550,- 350 + 550,- 300 + 550,- 350 + 550,- 300 + + 550,- 300 + 550,-1200 + 550,- 300 + 550,-1200 + + 550,- 300 + 550,- 300 + 600,- 300 + 550,- 300 + + 550,- 300 + 600,- 300 + 550,- 300 + 550,- 300 + + 550 +Sum: 54200 + + MSB first +Protocol=Panasonic Address=0xB Command=0x10 Raw-Data=0xA01000B02002 48 bits LSB first +Send with: IrSender.sendPanasonic(0xB, 0x10, ); +rawData[100]: + -1076300 + +3550,-1600 + + 550,- 350 + 550,-1150 + 550,- 300 + 600,- 300 + + 550,- 300 + 600,- 300 + 550,- 300 + 550,- 300 + + 600,- 300 + 550,- 300 + 550,- 300 + 600,- 300 + + 550,- 300 + 550,-1200 + 550,- 300 + 550,- 300 + + 550,- 350 + 550,- 300 + 550,- 300 + 550,- 350 + + 550,-1150 + 550,-1200 + 550,- 300 + 550,-1200 + + 550,- 300 + 550,- 300 + 550,- 350 + 550,- 300 + + 550,- 350 + 550,- 350 + 550,- 300 + 550,- 300 + + 550,- 350 + 550,- 300 + 550,- 300 + 550,- 350 + + 550,-1150 + 550,- 300 + 600,- 300 + 550,- 300 + + 550,- 300 + 600,- 300 + 550,- 300 + 550,- 300 + + 600,- 300 + 550,-1150 + 550,- 350 + 550,-1150 + + 550 +Sum: 54250 + +Send generic 72 bit PulseDistance 0x5A AFEDCBA9 87654321 LSB first +Protocol=PulseDistance Raw-Data=0x5A 72 bits LSB first +Send with: + uint64_t tRawData[]={0xAFEDCBA987654321, 0x5A}; + IrSender.sendPulseDistanceWidthFromArray(38, 9000, 4350, 650, 1600, 650, 500, &tRawData[0], 72, PROTOCOL_IS_LSB_FIRST, , ); +rawData[148]: + -1082050 + +9000,-4350 + + 700,-1600 + 650,- 500 + 700,- 500 + 650,- 500 + + 650,- 550 + 650,-1600 + 650,- 550 + 650,- 500 + + 700,-1600 + 650,-1650 + 650,- 500 + 650,- 500 + + 700,- 500 + 650,- 500 + 650,-1650 + 650,- 500 + + 650,-1650 + 650,- 500 + 650,-1650 + 650,- 500 + + 650,- 550 + 650,-1600 + 650,-1650 + 650,- 500 + + 650,-1650 + 650,-1600 + 700,-1600 + 650,- 500 + + 700,- 500 + 650,- 500 + 700,- 500 + 650,-1600 + + 700,-1600 + 650,- 500 + 700,- 500 + 650,-1600 + + 700,- 500 + 650,-1650 + 600,- 550 + 700,-1600 + + 650,-1650 + 650,-1600 + 650,- 550 + 650,-1600 + + 700,- 500 + 650,- 500 + 650,-1650 + 650,-1600 + + 650,-1650 + 650,- 500 + 700,-1600 + 650,-1650 + + 650,- 500 + 650,-1650 + 650,-1600 + 700,-1600 + + 650,-1650 + 650,-1600 + 700,-1600 + 650,-1650 + + 650,- 500 + 650,-1650 + 650,- 500 + 700,-1600 + + 650,- 500 + 700,-1600 + 650,- 550 + 650,-1600 + + 650,-1650 + 650,- 500 + 650,-1650 + 650,- 500 + + 650 +Sum: 140550 + +Send generic 52 bit PulseDistanceWidth 0xDCBA9 87654321 LSB first +Protocol=PulseWidth Raw-Data=0xDCBA987654321 52 bits LSB first +Send with: IrSender.sendPulseDistanceWidth(38, 400, 550, 750, 150, 400, 500, 0xDCBA987654321, 52, PROTOCOL_IS_LSB_FIRST, , ); +rawData[106]: + -1120100 + + 400,- 550 + + 700,- 150 + 450,- 500 + 400,- 550 + 400,- 500 + + 400,- 500 + 700,- 150 + 450,- 550 + 400,- 500 + + 700,- 150 + 800,- 150 + 450,- 500 + 400,- 500 + + 400,- 550 + 350,- 550 + 700,- 200 + 400,- 550 + + 700,- 200 + 400,- 500 + 750,- 200 + 400,- 500 + + 400,- 550 + 700,- 150 + 750,- 200 + 450,- 500 + + 700,- 150 + 750,- 200 + 750,- 150 + 450,- 500 + + 400,- 550 + 400,- 500 + 400,- 500 + 700,- 150 + + 800,- 150 + 450,- 500 + 400,- 500 + 750,- 150 + + 450,- 500 + 700,- 200 + 450,- 500 + 700,- 250 + + 700,- 150 + 800,- 150 + 450,- 500 + 700,- 200 + + 450,- 500 + 400,- 500 + 700,- 200 + 750,- 150 + + 750,- 150 + 450,- 550 + 700,- 200 + 700 +Sum: 48450 + +Send generic 32 bit PulseWidth 0x87654321 LSB first +Protocol=PulseWidth Raw-Data=0x87654321 32 bits LSB first +Send with: IrSender.sendPulseDistanceWidth(38, 1100, 400, 750, 200, 450, 200, 0x87654321, 32, PROTOCOL_IS_LSB_FIRST, , ); +rawData[66]: + -1090450 + +1100,- 400 + + 700,- 150 + 450,- 200 + 450,- 150 + 450,- 200 + + 400,- 200 + 750,- 150 + 450,- 150 + 450,- 200 + + 700,- 200 + 750,- 200 + 400,- 200 + 400,- 200 + + 450,- 150 + 450,- 150 + 750,- 150 + 450,- 200 + + 750,- 200 + 400,- 200 + 750,- 150 + 450,- 150 + + 450,- 200 + 700,- 200 + 750,- 200 + 400,- 200 + + 750,- 150 + 750,- 200 + 700,- 200 + 450,- 150 + + 450,- 200 + 400,- 200 + 450,- 150 + 750 +Sum: 24900 + +Send MagiQuest 0x6BCDFF00, 0x176 as generic 55 bit PulseDistanceWidth MSB first +Protocol=MagiQuest Address=0xFF00 Command=0x176 Raw-Data=0x6BCDFF00 56 bits MSB first +Send with: IrSender.sendMagiQuest(0x6BCDFF00, 0x176, ); +rawData[112]: + -1070250 + + 400,- 800 + 400,- 750 + 450,- 800 + 400,- 750 + + 400,- 800 + 400,- 750 + 400,- 800 + 400,- 750 + + 700,- 500 + 650,- 500 + 400,- 800 + 650,- 500 + + 400,- 800 + 650,- 500 + 700,- 500 + 650,- 500 + + 700,- 450 + 450,- 750 + 400,- 750 + 700,- 500 + + 700,- 450 + 400,- 800 + 700,- 450 + 700,- 500 + + 700,- 500 + 650,- 500 + 700,- 450 + 700,- 500 + + 700,- 450 + 700,- 500 + 700,- 450 + 400,- 800 + + 350,- 800 + 400,- 800 + 400,- 750 + 400,- 800 + + 400,- 750 + 400,- 800 + 400,- 750 + 700,- 450 + + 450,- 750 + 700,- 450 + 700,- 500 + 700,- 450 + + 400,- 800 + 650,- 500 + 700,- 500 + 400,- 750 + + 400,- 800 + 400,- 750 + 450,- 750 + 700,- 450 + + 700,- 500 + 400,- 750 + 400,- 800 + 700 +Sum: 65350 + +Send Onkyo (NEC with 16 bit command) +Protocol=Onkyo Address=0xFFF1 Command=0x7776 Raw-Data=0x7776FFF1 32 bits LSB first +Send with: IrSender.sendOnkyo(0xFFF1, 0x7776, ); +rawData[68]: + -1086400 + +9050,-4400 + + 700,-1600 + 650,- 450 + 700,- 450 + 650,- 500 + + 650,-1600 + 650,-1600 + 650,-1600 + 700,-1600 + + 650,-1600 + 650,-1600 + 650,-1600 + 700,-1600 + + 650,-1600 + 650,-1600 + 650,-1600 + 700,-1600 + + 650,- 450 + 700,-1600 + 650,-1600 + 650,- 500 + + 650,-1600 + 650,-1600 + 650,-1600 + 700,- 450 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,- 450 + + 700,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 650 +Sum: 77500 + +Send Apple +Protocol=Apple Address=0xF1 Command=0x76 Raw-Data=0xF17687EE 32 bits LSB first +Send with: IrSender.sendApple(0xF1, 0x76, ); +rawData[68]: + -1059500 + +9050,-4400 + + 700,- 500 + 650,-1600 + 700,-1600 + 650,-1600 + + 650,- 500 + 650,-1600 + 650,-1600 + 650,-1600 + + 700,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 650,- 450 + 700,- 450 + 700,- 450 + 650,-1600 + + 650,- 500 + 650,-1600 + 650,-1600 + 700,- 450 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,- 450 + + 700,-1600 + 650,- 450 + 650,- 500 + 650,- 500 + + 650,-1600 + 650,-1600 + 650,-1600 + 700,-1600 + + 600 +Sum: 73000 + +Send Panasonic +Protocol=Panasonic Address=0xFF1 Command=0x76 Raw-Data=0x9976FF102002 48 bits LSB first +Send with: IrSender.sendPanasonic(0xFF1, 0x76, ); +rawData[100]: + -1059200 + +3500,-1700 + + 500,- 350 + 500,-1250 + 500,- 400 + 500,- 350 + + 500,- 400 + 500,- 350 + 500,- 400 + 500,- 350 + + 500,- 400 + 500,- 350 + 500,- 400 + 500,- 350 + + 500,- 400 + 500,-1250 + 500,- 350 + 500,- 400 + + 500,- 350 + 500,- 400 + 450,- 400 + 500,- 400 + + 500,-1250 + 500,- 350 + 500,- 400 + 450,- 400 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,-1250 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,-1250 + + 500,- 400 + 500,-1250 + 500,-1250 + 500,- 350 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,- 400 + + 500,-1250 + 500,- 350 + 500,- 400 + 500,-1250 + + 500,-1250 + 500,- 400 + 500,- 350 + 500,-1250 + + 500 +Sum: 65200 + +Send Kaseikyo with 0x4711 as Vendor ID +Protocol=Kaseikyo Address=0xFF1 Command=0x76 Extra=0x4711 Raw-Data=0x9A76FF134711 48 bits LSB first +Send with: IrSender.sendKaseikyo(0xFF1, 0x76, , 0x4711); +rawData[100]: + -1079950 + +3550,-1650 + + 500,-1250 + 500,- 400 + 500,- 350 + 500,- 400 + + 500,-1250 + 500,- 350 + 550,- 350 + 500,- 400 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,- 350 + + 500,- 400 + 500,- 350 + 500,-1250 + 500,- 400 + + 500,-1250 + 500,-1250 + 500,- 350 + 500,- 400 + + 500,-1250 + 500,- 350 + 500,- 400 + 500,- 350 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,-1250 + + 500,-1250 + 500,-1250 + 500,-1250 + 550,-1200 + + 550,- 350 + 500,-1250 + 500,-1250 + 500,- 350 + + 500,-1250 + 500,-1250 + 500,-1250 + 550,- 350 + + 500,- 350 + 500,-1250 + 550,- 350 + 500,-1250 + + 500,-1250 + 500,- 350 + 500,- 400 + 500,-1250 + + 500 +Sum: 70500 + +Send Kaseikyo_Denon variant +Protocol=Kaseikyo_Denon Address=0xFF1 Command=0x76 Raw-Data=0x9976FF103254 48 bits LSB first +Send with: IrSender.sendKaseikyo_Denon(0xFF1, 0x76, ); +rawData[100]: + -1080650 + +3550,-1650 + + 550,- 350 + 500,- 350 + 550,-1200 + 550,- 350 + + 500,-1250 + 500,- 350 + 550,-1200 + 500,- 400 + + 500,- 350 + 500,-1250 + 550,- 350 + 500,- 350 + + 550,-1200 + 550,-1200 + 550,- 350 + 500,- 350 + + 550,- 350 + 500,- 350 + 550,- 350 + 500,- 350 + + 550,-1200 + 550,- 350 + 500,- 350 + 550,- 350 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,-1250 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,-1250 + + 500,- 350 + 500,-1250 + 500,-1250 + 500,- 400 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,- 350 + + 500,-1250 + 550,- 350 + 500,- 350 + 550,-1250 + + 450,-1300 + 500,- 350 + 500,- 400 + 500,-1250 + + 500 +Sum: 68750 + +Send Denon +Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0xED1 15 bits LSB first +Send with: IrSender.sendDenon(0x11, 0x76, ); +rawData[32]: + -1078400 + + 350,-1750 + 350,- 700 + 350,- 700 + 350,- 700 + + 350,-1750 + 350,- 700 + 300,-1800 + 350,-1750 + + 350,- 700 + 350,-1750 + 350,-1750 + 350,-1750 + + 350,- 700 + 350,- 700 + 350,- 700 + 350 +Sum: 23450 + +Send Denon/Sharp variant +Protocol=Sharp Address=0x11 Command=0x76 Raw-Data=0x4ED1 15 bits LSB first +Send with: IrSender.sendSharp(0x11, 0x76, ); +rawData[32]: + -1023500 + + 350,-1750 + 350,- 700 + 350,- 700 + 350,- 700 + + 350,-1750 + 350,- 700 + 350,-1750 + 350,-1750 + + 350,- 700 + 350,-1750 + 350,-1750 + 350,-1750 + + 350,- 700 + 300,- 750 + 350,-1750 + 350 +Sum: 24500 + +Send Sony/SIRCS with 7 command and 5 address bits +Protocol=Sony Address=0x11 Command=0x76 Raw-Data=0x8F6 12 bits LSB first +Send with: IrSender.sendSony(0x11, 0x76, 2, 12); +rawData[26]: + -1025750 + +2500,- 500 + + 700,- 500 +1300,- 650 +1250,- 550 + 700,- 500 + +1300,- 500 +1300,- 500 +1300,- 550 +1300,- 500 + + 700,- 500 + 700,- 500 + 700,- 550 +1250 +Sum: 21300 + +Send Sony/SIRCS with 7 command and 8 address bits +Protocol=Sony Address=0xF1 Command=0x76 Raw-Data=0x78F6 15 bits LSB first +Send with: IrSender.sendSony(0xF1, 0x76, 2, 15); +rawData[32]: + -1037600 + +2500,- 550 + + 650,- 550 +1300,- 500 +1300,- 500 + 700,- 500 + +1300,- 550 +1250,- 550 +1300,- 500 +1300,- 500 + + 700,- 500 + 700,- 550 + 650,- 550 +1300,- 500 + +1300,- 550 +1300,- 500 +1300 +Sum: 26700 + +Send Sony/SIRCS with 7 command and 13 address bits +Protocol=Sony Address=0x1FF1 Command=0x76 Raw-Data=0xFF8F6 20 bits LSB first +Send with: IrSender.sendSony(0x1FF1, 0x76, 2, 20); +rawData[42]: + -1041100 + +2500,- 500 + + 700,- 550 +1300,- 500 +1300,- 500 + 700,- 550 + +1250,- 550 +1300,- 500 +1300,- 550 +1300,- 500 + + 700,- 500 + 700,- 500 + 700,- 500 +1300,- 550 + +1300,- 500 +1300,- 500 +1300,- 500 +1300,- 550 + +1300,- 500 +1300,- 500 +1300,- 550 +1250 +Sum: 35750 + +Send Samsung 8 bit command +Protocol=Samsung Address=0xFFF1 Command=0x76 Raw-Data=0x8976FFF1 32 bits LSB first +Send with: IrSender.sendSamsung(0xFFF1, 0x76, ); +rawData[68]: + -1044850 + +4550,-4400 + + 700,-1600 + 650,- 450 + 700,- 450 + 650,- 500 + + 650,-1600 + 650,-1600 + 650,-1600 + 700,-1600 + + 650,-1600 + 650,-1600 + 650,-1600 + 700,-1600 + + 650,-1600 + 650,-1600 + 650,-1600 + 700,-1600 + + 650,- 450 + 700,-1600 + 650,-1600 + 650,- 500 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,- 450 + + 700,-1600 + 650,- 450 + 650,- 500 + 650,-1600 + + 650,- 500 + 650,- 450 + 700,- 450 + 650,-1600 + + 650 +Sum: 69600 + +Send Samsung 16 bit command +Protocol=Samsung Address=0xFFF1 Command=0x9876 Raw-Data=0x9876FFF1 32 bits LSB first +Send with: IrSender.sendSamsung(0xFFF1, 0x9876, ); +rawData[68]: + -1061000 + +4600,-4400 + + 650,-1600 + 700,- 450 + 650,- 500 + 650,- 450 + + 700,-1550 + 700,-1600 + 650,-1600 + 650,-1600 + + 650,-1600 + 700,-1600 + 650,-1600 + 650,-1600 + + 650,-1600 + 700,-1600 + 650,-1600 + 650,-1600 + + 650,- 500 + 650,-1600 + 700,-1600 + 650,- 450 + + 700,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 650,- 450 + 700,- 450 + 650,- 500 + 650,-1600 + + 650,-1600 + 650,- 500 + 650,- 450 + 700,-1600 + + 650 +Sum: 69650 + +Send Samsung48 16 bit command +Protocol=Samsung48 Address=0xFFF1 Command=0x9876 Raw-Data=0xFFFFFFFF8976FFF1 48 bits LSB first +Send with: IrSender.sendSamsung48(0xFFF1, 0x9876, ); +rawData[100]: + -1066350 + +4550,-4400 + + 650,-1650 + 600,- 500 + 700,- 450 + 650,- 500 + + 650,-1600 + 650,-1600 + 650,-1600 + 700,-1600 + + 600,-1650 + 650,-1600 + 650,-1600 + 700,-1600 + + 600,-1650 + 650,-1600 + 650,-1600 + 700,-1550 + + 700,- 450 + 650,-1600 + 700,-1600 + 600,- 500 + + 650,-1600 + 700,-1600 + 650,-1600 + 650,- 500 + + 650,-1600 + 650,- 500 + 650,- 500 + 650,-1600 + + 650,- 450 + 700,- 450 + 650,- 500 + 650,-1600 + + 650,- 500 + 650,- 450 + 650,- 500 + 650,-1600 + + 650,-1600 + 700,- 450 + 650,- 500 + 650,-1600 + + 650,-1600 + 650,-1600 + 650,-1650 + 650,- 450 + + 650,- 500 + 650,-1600 + 650,-1600 + 650,- 500 + + 650 +Sum: 96750 + +Send RC5 +Protocol=RC5 Address=0x11 Command=0x36 Raw-Data=0x1476 13 bits MSB first +Send with: IrSender.sendRC5(0x11, 0x36, ); +rawData[20]: + -1079150 + +1000,- 800 + +1900,-1650 +1900,- 800 +1000,- 800 +1000,-1700 + +1000,- 800 +1000,- 750 +1900,-1700 +1000,- 800 + +1900 +Sum: 23400 + +Send RC5X with 7.th MSB of command set +Protocol=RC5 Address=0x11 Command=0x76 Toggle=1 Raw-Data=0xC76 13 bits MSB first +Send with: IrSender.sendRC5(0x11, 0x76, ); +rawData[20]: + -1035450 + +1900,-1700 + +1000,- 800 +1900,- 750 +1000,- 800 +1000,-1700 + +1000,- 800 +1000,- 800 +1900,-1650 +1000,- 800 + +1900 +Sum: 23400 + +Send RC6 +Protocol=RC6 Address=0xF1 Command=0x76 Raw-Data=0xF176 20 bits MSB first +Send with: IrSender.sendRC6(0xF1, 0x76, ); +rawData[36]: + -1032650 + +2750,- 800 + + 550,- 800 + 550,- 350 + 550,- 350 + 550,- 800 + +1450,- 350 + 550,- 350 + 550,- 350 + 550,- 800 + + 550,- 350 + 550,- 350 +1000,- 800 +1000,- 350 + + 550,- 350 + 550,- 800 +1000,- 350 + 550,- 800 + + 550 +Sum: 23450 + +Send RC6A with 14 bit 0x2711 as extra +Protocol=RC6A Address=0xF1 Command=0x76 Extra=0x2711 Toggle=1 Raw-Data=0xA711F176 35 bits MSB first +Send with: IrSender.sendRC6A(0xF1, 0x76, , 0x2711); +rawData[58]: + -1032900 + +2650,- 900 + + 450,- 450 + 450,- 450 + 450,- 850 +1400,-1300 + + 950,- 850 + 500,- 400 + 950,- 400 + 500,- 450 + + 450,- 850 + 500,- 400 + 500,- 400 + 950,- 850 + + 500,- 400 + 500,- 400 + 950,- 450 + 450,- 450 + + 450,- 450 + 450,- 400 + 500,- 850 + 500,- 400 + + 500,- 400 + 950,- 850 + 950,- 400 + 500,- 400 + + 500,- 850 + 950,- 450 + 450,- 850 + 500 +Sum: 36850 + +Send Bang&Olufsen +- ENABLE_BEO_WITHOUT_FRAME_GAP is enabled +- Now print raw data and try to decode the first 6 entries, which results in rawData 0x0! +rawData[36]: + -1032400 + + 300,-2850 + + 250,-2850 + 250,-15200 + 300,-2850 + 250,-9000 + + 300,-5950 + 250,-5950 + 250,-5900 + 300,-2850 + + 250,-5950 + 250,-9050 + 250,-2850 + 250,-9000 + + 300,-2850 + 250,-5950 + 250,-5950 + 250,-5950 + + 250 +Sum: 105700 +Protocol=Bang&Olufsen Address=0x0 Command=0x0 Raw-Data=0x0 0 bits MSB first + +- Remove trailing 6 entries, which is equivalent to define RECORD_GAP_MICROS < 15000, to enable successful B&O decode +Protocol=Bang&Olufsen Address=0xF2 Command=0x87 Raw-Data=0xF287 16 bits MSB first +Send with: IrSender.sendBang&Olufsen(0xF2, 0x87, ); +rawData[38]: + -15200 + + 300,-2850 + + 250,-9000 + 300,-5950 + 250,-5950 + 250,-5900 + + 300,-2850 + 250,-5950 + 250,-9050 + 250,-2850 + + 250,-9000 + 300,-2850 + 250,-5950 + 250,-5950 + + 250,-5950 + 250,-9050 + 250,-5900 + 300,-5950 + + 250,-12100 + 300 +Sum: 118100 + +Send MagiQuest +Protocol=MagiQuest Address=0xF2 Command=0x187 Raw-Data=0x6BCD00F2 56 bits MSB first +Send with: IrSender.sendMagiQuest(0x6BCD00F2, 0x187, ); +rawData[112]: + -1088750 + + 350,- 800 + 350,- 800 + 350,- 800 + 350,- 800 + + 350,- 800 + 350,- 800 + 350,- 800 + 350,- 850 + + 600,- 550 + 600,- 550 + 350,- 750 + 650,- 550 + + 300,- 850 + 600,- 550 + 600,- 550 + 600,- 550 + + 600,- 550 + 350,- 800 + 350,- 750 + 650,- 500 + + 650,- 500 + 350,- 800 + 650,- 500 + 350,- 800 + + 350,- 800 + 350,- 800 + 350,- 800 + 350,- 800 + + 350,- 800 + 350,- 800 + 350,- 800 + 650,- 500 + + 650,- 500 + 650,- 500 + 650,- 500 + 350,- 800 + + 350,- 800 + 650,- 500 + 350,- 850 + 600,- 550 + + 600,- 550 + 350,- 800 + 300,- 800 + 350,- 800 + + 350,- 800 + 650,- 500 + 650,- 500 + 650,- 550 + + 350,- 800 + 300,- 800 + 650,- 500 + 350,- 800 + + 350,- 800 + 350,- 800 + 650,- 500 + 350 +Sum: 63600 + +Send next protocols with IrSender.write + +Send JVC +Protocol=JVC Address=0xF1 Command=0x76 Raw-Data=0x76F1 16 bits LSB first +Send with: IrSender.sendJVC(0xF1, 0x76, ); +rawData[36]: + -1040750 + +8500,-4150 + + 650,-1500 + 650,- 400 + 650,- 450 + 600,- 450 + + 650,-1500 + 600,-1500 + 650,-1500 + 600,-1550 + + 600,- 450 + 650,-1500 + 650,-1450 + 650,- 450 + + 650,-1450 + 650,-1500 + 650,-1500 + 600,- 450 + + 650 +Sum: 41050 + +Send LG +Protocol=LG Address=0xF1 Command=0x9876 Raw-Data=0xF19876E 28 bits MSB first +Send with: IrSender.sendLG(0xF1, 0x9876, ); +rawData[60]: + -1059650 + +9100,-4100 + + 650,-1500 + 600,-1500 + 600,-1500 + 600,-1500 + + 600,- 500 + 600,- 450 + 600,- 450 + 650,-1500 + + 600,-1500 + 600,- 450 + 600,- 500 + 600,-1500 + + 600,-1500 + 600,- 500 + 600,- 450 + 600,- 450 + + 650,- 450 + 600,-1500 + 600,-1500 + 600,-1500 + + 650,- 450 + 600,-1500 + 600,-1500 + 600,- 500 + + 600,-1500 + 600,-1500 + 600,-1500 + 600,- 500 + + 600 +Sum: 60450 + +Send Bosewave with no address and 8 command bits +Protocol=BoseWave Address=0x0 Command=0x76 Raw-Data=0x8976 16 bits LSB first +Send with: IrSender.sendBoseWave(0x0, 0x76, ); +rawData[36]: + -1050250 + +1100,-1400 + + 600,- 400 + 600,-1400 + 600,-1400 + 600,- 400 + + 600,-1400 + 600,-1400 + 650,-1400 + 600,- 400 + + 600,-1400 + 600,- 400 + 600,- 400 + 600,-1400 + + 600,- 400 + 600,- 400 + 600,- 400 + 600,-1400 + + 600 +Sum: 27150 + +Send FAST +Protocol=FAST Address=0x0 Command=0x76 Raw-Data=0x8976 16 bits LSB first +Send with: IrSender.sendFAST(0x0, 0x76, ); +rawData[36]: + -1041350 + +2200,-1000 + + 650,- 400 + 650,-1500 + 650,-1500 + 600,- 450 + + 650,-1500 + 650,-1500 + 600,-1500 + 650,- 450 + + 600,-1500 + 650,- 450 + 600,- 450 + 650,-1500 + + 600,- 450 + 650,- 450 + 600,- 450 + 650,-1500 + + 600 +Sum: 29450 + +Force buffer overflow by sending 280 marks and spaces +Overflow +Try to increase the "RAW_BUFFER_LENGTH" value of 200 in UnitTest.cpp +rawData[200]: + -1039850 + + 300,- 450 + + 300,- 450 + 350,- 450 + 300,- 450 + 350,- 450 + + 300,- 450 + 350,- 450 + 300,- 450 + 300,- 500 + + 300,- 450 + 350,- 450 + 300,- 450 + 350,- 450 + + 300,- 450 + 350,- 450 + 300,- 450 + 350,- 450 + + 300,- 450 + 350,- 450 + 300,- 450 + 350,- 450 + + 300,- 500 + 300,- 450 + 350,- 450 + 300,- 500 + + 350,- 450 + 300,- 450 + 350,- 450 + 300,- 450 + + 350,- 400 + 350,- 450 + 300,- 450 + 350,- 450 + + 300,- 450 + 350,- 450 + 300,- 450 + 350,- 450 + + 300,- 450 + 300,- 500 + 300,- 450 + 300,- 500 + + 300,- 450 + 350,- 450 + 300,- 450 + 350,- 450 + + 300,- 450 + 350,- 450 + 300,- 450 + 350,- 450 + + 300,- 450 + 350,- 450 + 300,- 450 + 350,- 450 + + 300,- 450 + 350,- 450 + 300,- 450 + 350,- 450 + + 350,- 450 + 300,- 450 + 300,- 450 + 350,- 450 + + 300,- 450 + 350,- 450 + 300,- 450 + 350,- 450 + + 300,- 450 + 350,- 400 + 350,- 450 + 300,- 450 + + 350,- 450 + 300,- 450 + 350,- 450 + 300,- 450 + + 350,- 450 + 300,- 450 + 350,- 450 + 300,- 450 + + 350,- 450 + 300,- 450 + 350,- 450 + 300,- 450 + + 350,- 450 + 300,- 450 + 300,- 500 + 300,- 450 + + 350,- 450 + 300,- 450 + 300,- 500 + 300,- 450 + + 350,- 450 + 350,- 450 + 300,- 450 + 300,- 450 + + 350,- 450 + 300,- 450 + 350,- 450 + 300,- 450 + + 350,- 450 + 300,- 450 + 350 +Sum: 77050 +ERROR: Unknown protocol + + +address=0xF2 command=0x87 + +Send NEC with 8 bit address and complete NEC frames as repeats to force decoding as NEC2 +Protocol=NEC Address=0xF2 Command=0x87 Raw-Data=0x78870DF2 32 bits LSB first +Send with: IrSender.sendNEC(0xF2, 0x87, ); +rawData[68]: + -3276750 + +8900,-4450 + + 600,- 500 + 650,-1600 + 600,- 550 + 600,- 500 + + 650,-1600 + 600,-1650 + 600,-1650 + 600,-1650 + + 600,-1600 + 600,- 550 + 600,-1600 + 650,-1600 + + 600,- 550 + 600,- 500 + 600,- 550 + 600,- 500 + + 600,-1650 + 600,-1650 + 600,-1650 + 600,- 550 + + 600,- 500 + 600,- 550 + 600,- 550 + 600,-1600 + + 600,- 550 + 600,- 500 + 650,- 500 + 600,-1650 + + 600,-1650 + 600,-1600 + 600,-1650 + 600,- 550 + + 600 +Sum: 67850 + +Protocol=NEC2 Address=0xF2 Command=0x87 Repeat gap=65900us Raw-Data=0x78870DF2 32 bits LSB first +Send with: IrSender.sendNEC2(0xF2, 0x87, ); +rawData[68]: + -65900 + +8900,-4450 + + 600,- 500 + 600,-1650 + 600,- 500 + 600,- 550 + + 600,-1650 + 600,-1600 + 600,-1650 + 600,-1650 + + 600,-1650 + 600,- 500 + 600,-1650 + 600,-1650 + + 600,- 500 + 600,- 550 + 600,- 550 + 600,- 500 + + 600,-1650 + 600,-1650 + 600,-1600 + 600,- 550 + + 600,- 550 + 600,- 500 + 600,- 550 + 600,-1650 + + 600,- 500 + 600,- 550 + 600,- 500 + 650,-1600 + + 600,-1650 + 600,-1600 + 650,-1600 + 600,- 550 + + 600 +Sum: 67800 + +Send NEC with 16 bit address +Protocol=NEC Address=0xF2 Command=0x87 Raw-Data=0x78870DF2 32 bits LSB first +Send with: IrSender.sendNEC(0xF2, 0x87, ); +rawData[68]: + -1060000 + +9100,-4400 + + 650,- 450 + 700,-1600 + 650,- 450 + 700,- 450 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,-1600 + + 650,-1600 + 650,- 500 + 650,-1600 + 650,-1600 + + 650,- 500 + 650,- 500 + 600,- 500 + 650,- 500 + + 650,-1600 + 650,-1600 + 650,-1600 + 700,- 450 + + 650,- 500 + 600,- 500 + 650,- 500 + 650,-1600 + + 650,- 500 + 650,- 450 + 700,- 450 + 650,-1600 + + 700,-1550 + 700,-1600 + 650,-1600 + 650,- 500 + + 650 +Sum: 68450 + +Send NEC2 with 16 bit address +Protocol=NEC Address=0xF2 Command=0x87 Raw-Data=0x78870DF2 32 bits LSB first +Send with: IrSender.sendNEC(0xF2, 0x87, ); +rawData[68]: + -1060100 + +9050,-4400 + + 700,- 450 + 650,-1600 + 650,- 500 + 650,- 450 + + 700,-1600 + 650,-1600 + 650,-1600 + 650,-1600 + + 700,-1600 + 650,- 450 + 700,-1600 + 650,-1600 + + 650,- 500 + 600,- 500 + 650,- 500 + 650,- 450 + + 700,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 650,- 450 + 700,- 450 + 650,- 500 + 650,-1600 + + 650,- 500 + 650,- 500 + 650,- 450 + 650,-1600 + + 700,-1600 + 600,-1650 + 650,-1600 + 650,- 500 + + 650 +Sum: 68450 + +Send Onkyo (NEC with 16 bit command) +Protocol=Onkyo Address=0xF2 Command=0x8887 Raw-Data=0x888700F2 32 bits LSB first +Send with: IrSender.sendOnkyo(0xF2, 0x8887, ); +rawData[68]: + -1060700 + +9100,-4400 + + 650,- 450 + 700,-1600 + 650,- 450 + 700,- 450 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,-1600 + + 650,- 450 + 700,- 450 + 650,- 500 + 650,- 450 + + 700,- 450 + 650,- 500 + 650,- 450 + 650,- 500 + + 650,-1600 + 650,-1600 + 700,-1550 + 700,- 450 + + 650,- 500 + 650,- 500 + 650,- 450 + 650,-1600 + + 700,- 450 + 650,- 500 + 650,- 450 + 650,-1650 + + 650,- 500 + 650,- 450 + 700,- 450 + 650,-1600 + + 650 +Sum: 62800 + +Send Apple +Protocol=Apple Address=0xF2 Command=0x87 Raw-Data=0xF28787EE 32 bits LSB first +Send with: IrSender.sendApple(0xF2, 0x87, ); +rawData[68]: + -1059150 + +9050,-4400 + + 650,- 500 + 650,-1600 + 650,-1600 + 700,-1600 + + 650,- 450 + 650,-1600 + 700,-1600 + 650,-1600 + + 650,-1600 + 650,-1600 + 700,-1600 + 650,- 450 + + 700,- 450 + 650,- 500 + 650,- 450 + 650,-1600 + + 700,-1600 + 650,-1600 + 650,-1600 + 650,- 500 + + 650,- 450 + 700,- 450 + 650,- 500 + 650,-1600 + + 650,- 500 + 600,-1650 + 650,- 500 + 650,- 450 + + 650,-1600 + 650,-1650 + 650,-1600 + 650,-1600 + + 650 +Sum: 71800 + +Send Panasonic +Protocol=Panasonic Address=0xF2 Command=0x87 Raw-Data=0xA8870F202002 48 bits LSB first +Send with: IrSender.sendPanasonic(0xF2, 0x87, ); +rawData[100]: + -1069050 + +3550,-1650 + + 500,- 400 + 500,-1250 + 500,- 350 + 500,- 400 + + 500,- 350 + 500,- 400 + 500,- 350 + 500,- 400 + + 500,- 350 + 500,- 400 + 500,- 350 + 500,- 400 + + 500,- 350 + 500,-1250 + 500,- 400 + 500,- 350 + + 500,- 400 + 500,- 350 + 500,- 400 + 500,- 350 + + 500,- 400 + 450,-1300 + 500,- 350 + 550,- 350 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,-1250 + + 500,- 350 + 500,- 400 + 500,- 350 + 500,- 400 + + 500,-1250 + 500,-1250 + 450,-1300 + 500,- 350 + + 500,- 400 + 450,- 400 + 500,- 400 + 450,-1300 + + 500,- 350 + 550,- 350 + 500,- 350 + 500,-1250 + + 550,- 350 + 500,-1250 + 450,- 400 + 550,-1200 + + 550 +Sum: 60000 + +Send Kaseikyo with 0x4711 as Vendor ID +Protocol=Kaseikyo Address=0xF2 Command=0x87 Extra=0x4711 Raw-Data=0xAB870F234711 48 bits LSB first +Send with: IrSender.sendKaseikyo(0xF2, 0x87, , 0x4711); +rawData[100]: + -1079750 + +3550,-1650 + + 550,-1200 + 550,- 350 + 500,- 350 + 550,- 350 + + 500,-1250 + 500,- 350 + 550,- 350 + 500,- 350 + + 550,-1200 + 550,-1200 + 550,-1200 + 550,- 350 + + 500,- 350 + 550,- 350 + 500,-1250 + 500,- 350 + + 550,-1200 + 550,-1200 + 550,- 350 + 500,- 350 + + 550,- 350 + 500,-1250 + 500,- 350 + 550,- 350 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,-1250 + + 500,- 300 + 550,- 400 + 500,- 350 + 550,- 350 + + 500,-1250 + 500,-1250 + 500,-1250 + 500,- 350 + + 500,- 400 + 500,- 350 + 500,- 400 + 500,-1250 + + 500,-1250 + 500,-1250 + 500,- 350 + 500,-1250 + + 500,- 400 + 500,-1250 + 500,- 350 + 500,-1250 + + 550 +Sum: 67000 \ No newline at end of file diff --git a/keywords.txt b/keywords.txt index f78b7eaff..adfe84678 100644 --- a/keywords.txt +++ b/keywords.txt @@ -9,52 +9,132 @@ decode_results KEYWORD1 IRrecv KEYWORD1 IRsend KEYWORD1 +IrReceiver KEYWORD1 +IrSender KEYWORD1 +decodedIRData KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### - -blink13 KEYWORD2 +# IRReceive +available KEYWORD2 +begin KEYWORD2 decode KEYWORD2 +disableIRIn KEYWORD2 enableIRIn KEYWORD2 +isIdle KEYWORD2 +printActiveIRProtocols KEYWORD2 +printIRResultMinimal KEYWORD2 +printIRResultRawFormatted KEYWORD2 +printIRResultShort KEYWORD2 +printIRSendUsage KEYWORD2 +registerReceiveCompleteCallback KEYWORD2 +restartAfterSend KEYWORD2 +restartTimer KEYWORD2 +restartTimerWithTicksToAdd KEYWORD2 resume KEYWORD2 -enableIROut KEYWORD2 -sendNEC KEYWORD2 -sendSony KEYWORD2 -sendSanyo KEYWORD2 -sendMitsubishi KEYWORD2 +setReceivePin KEYWORD2 +start KEYWORD2 +read KEYWORD2 +stop KEYWORD2 +end KEYWORD2 + +# IRSend +setSendPin KEYWORD2 +write KEYWORD2 sendRaw KEYWORD2 +sendJVC KEYWORD2 +sendLG KEYWORD2 +sendLG2 KEYWORD2 +sendLG2Repeat KEYWORD2 +sendLGRaw KEYWORD2 +sendNEC KEYWORD2 +sendNEC2 KEYWORD2 +sendNECRepeat KEYWORD2 +sendNECRaw KEYWORD2 +sendOnkyo KEYWORD2 +sendApple KEYWORD2 +sendPanasonic KEYWORD2 +sendKaseikyo KEYWORD2 +sendKaseikyo_Denon KEYWORD2 +sendKaseikyo_Sharp KEYWORD2 +sendKaseikyo_JVC KEYWORD2 +sendKaseikyo_Mitsubishi KEYWORD2 sendRC5 KEYWORD2 sendRC6 KEYWORD2 -sendDISH KEYWORD2 -sendSAMSUNG KEYWORD2 +sendSamsungRepeat KEYWORD2 +sendSamsung KEYWORD2 sendSharp KEYWORD2 +sendSony KEYWORD2 sendSharpRaw KEYWORD2 -sendSharpAlt KEYWORD2 -sendSharpAltRaw KEYWORD2 -sendPanasonic KEYWORD2 -sendJVC KEYWORD2 -sendLG KEYWORD2 +sendLegoPowerFunctions KEYWORD2 +sendMagiQuest KEYWORD2 +sendPronto KEYWORD2 sendMagiQuest KEYWORD2 +sendFAST KEYWORD2 + +# IRFeedbackLED +disableLEDFeedback KEYWORD2 +disableLEDFeedbackForSend KEYWORD2 +enableLEDFeedback KEYWORD2 +enableLEDFeedbackForSend KEYWORD2 +setFeedbackLED KEYWORD2 +setLEDFeedback KEYWORD2 + +# TinyIRReceiver +disablePCIInterruptForTinyReceiver KEYWORD2 +enablePCIInterruptForTinyReceiver KEYWORD2 +initPCIInterruptForTinyReceiver KEYWORD2 +isIRReceiverAttachedForTinyReceiver KEYWORD2 +printTinyReceiverResultMinimal KEYWORD2 +TinyReceiverDecode KEYWORD2 +# TinyIRSender +sendExtendedNEC KEYWORD2 +sendFAST KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### +UNKNOWN LITERAL1 +PULSE_DISTANCE LITERAL1 +PULSE_WIDTH LITERAL1 +APPLE LITERAL1 +DENON LITERAL1 +JVC LITERAL1 +LG LITERAL1 +LG2 LITERAL1 NEC LITERAL1 -SONY LITERAL1 -SANYO LITERAL1 -MITSUBISHI LITERAL1 +NEC2 LITERAL1 +ONKYO LITERAL1 +PANASONIC LITERAL1 +KASEIKYO LITERAL1 +KASEIKYO_JVC LITERAL1 +KASEIKYO_DENON LITERAL1 +KASEIKYO_SHARP LITERAL1 +KASEIKYO_MITSUBISHI LITERAL1 RC5 LITERAL1 RC6 LITERAL1 -DISH LITERAL1 -SAMSUNGL ITERAL1 +RC6A LITERAL1 +SAMSUNG LITERAL1 +SAMSUNGLG LITERAL1 +SAMSUNG48 LITERAL1 SHARP LITERAL1 -SHARP_ALT LITERAL1 -PANASONIC LITERAL1 -JVC LITERAL1 -LG LITERAL1 -AIWA_RC_T501 LITERAL1 +SONY LITERAL1 +BANG_OLUFSEN LITERAL1 +BOSEWAVE LITERAL1 +LEGO_PF LITERAL1 MAGIQUEST LITERAL1 -UNKNOWN LITERAL1 -REPEAT LITERAL1 +WHYNTER LITERAL1 +FAST LITERAL1 + +FEEDBACK_LED_IS_ACTIVE_LOW LITERAL1 + +RAW_BUFFER_LENGTH LITERAL1 +EXCLUDE_UNIVERSAL_PROTOCOLS LITERAL1 +EXCLUDE_EXOTIC_PROTOCOLS LITERAL1 +SEND_PWM_BY_TIMER LITERAL1 +USE_NO_SEND_PWM LITERAL1 +IR_RECEIVE_PIN LITERAL1 +IR_SEND_PIN LITERAL1 +USE_CALLBACK_FOR_TINY_RECEIVER LITERAL1 diff --git a/library.json b/library.json index b0e8c07b4..c0666baa1 100644 --- a/library.json +++ b/library.json @@ -1,24 +1,34 @@ { "name": "IRremote", - "keywords": "infrared, ir, remote", + "version": "4.4.2", "description": "Send and receive infrared signals with multiple protocols", + "keywords": "communication, infrared, ir, remote", + "homepage": "https://github.com/Arduino-IRremote/Arduino-IRremote", "repository": { "type": "git", - "url": "https://github.com/z3t0/Arduino-IRremote.git" + "url": "https://github.com/Arduino-IRremote/Arduino-IRremote.git" }, - "version": "2.8.0", - "frameworks": "arduino", - "platforms": "atmelavr", "authors" : [ + { + "name":"Armin Joachimsmeyer", + "email":"armin.arduino@gmail.com", + "maintainer": true + }, { "name":"Rafi Khan", - "email":"zetoslab@gmail.com" + "email":"rafi@rafikhan.io" }, { "name":"Ken Shirriff", "email":"ken.shirriff@gmail.com" } - ] + ], + "license": "MIT", + "frameworks": "arduino", + "platforms": ["atmelavr", "atmelmegaavr", "atmelsam", "espressif8266", "espressif32", "ststm32"], + "headers": "IRRemote.hpp", + "examples": "examples/*/*.ino", + "export": {"exclude": [".github", "pictures"]} } diff --git a/library.properties b/library.properties index 576d0ecac..40e043e2a 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,10 @@ name=IRremote -version=2.8.0 -author=shirriff, z3t0 -maintainer=Armin Joachimsmeyer +version=4.4.2 +author=shirriff, z3t0, ArminJo +maintainer=Armin Joachimsmeyer sentence=Send and receive infrared signals with multiple protocols -paragraph=Currently included protocols: BoseWave, Denon, Dish, JVC, Lego, LG, MagiQuest, NEC, Panasonic, RC5, RC6, Samsung, Sanyo, Sharp, Sony, Whynter, (Pronto).

New: Changed License to MIT, ATtiny timer 1 support, Removed AIWA + Mitsubishi, example IR2Keyboard.
+paragraph=Currently included protocols: Denon / Sharp, JVC, LG / LG2, NEC / Onkyo / Apple, Panasonic / Kaseikyo, RC5, RC6, Samsung, Sony, (Pronto), BangOlufsen, BoseWave, Lego, Whynter, FAST, MagiQuest, Universal Pulse Distance and Pulse Width. category=Communication -url=https://github.com/z3t0/Arduino-IRremote -architectures=avr,megaavr,samd,esp32,mbed +url=https://github.com/Arduino-IRremote/Arduino-IRremote +architectures=avr,megaavr,samd,esp8266,esp32,stm32,STM32F1,mbed,mbed_nano,rp2040,mbed_rp2040,renesas_uno +includes=IRremote.hpp diff --git a/pictures/BoseWaveMusicSystem.jpg b/pictures/BoseWaveMusicSystem.jpg new file mode 100644 index 000000000..6f11962eb Binary files /dev/null and b/pictures/BoseWaveMusicSystem.jpg differ diff --git a/pictures/BoseWaveSoundTouch_IV.jpg b/pictures/BoseWaveSoundTouch_IV.jpg new file mode 100644 index 000000000..17d535d90 Binary files /dev/null and b/pictures/BoseWaveSoundTouch_IV.jpg differ diff --git a/pictures/IRReceiverPinout.jpg b/pictures/IRReceiverPinout.jpg new file mode 100644 index 000000000..5e232d157 Binary files /dev/null and b/pictures/IRReceiverPinout.jpg differ diff --git a/pictures/IR_PWM_by_software_detail.png b/pictures/IR_PWM_by_software_detail.png new file mode 100644 index 000000000..5d5e8d3ee Binary files /dev/null and b/pictures/IR_PWM_by_software_detail.png differ diff --git a/pictures/IR_PWM_by_software_jitter.png b/pictures/IR_PWM_by_software_jitter.png new file mode 100644 index 000000000..25479711f Binary files /dev/null and b/pictures/IR_PWM_by_software_jitter.png differ diff --git a/pictures/IR_RobotCar.jpg b/pictures/IR_RobotCar.jpg new file mode 100644 index 000000000..6258d0136 Binary files /dev/null and b/pictures/IR_RobotCar.jpg differ diff --git a/pictures/IR_UnitTest_delay.bmp b/pictures/IR_UnitTest_delay.bmp new file mode 100644 index 000000000..00e17dd49 Binary files /dev/null and b/pictures/IR_UnitTest_delay.bmp differ diff --git a/pictures/LGRemote1.jpg b/pictures/LGRemote1.jpg new file mode 100644 index 000000000..11b3debfa Binary files /dev/null and b/pictures/LGRemote1.jpg differ diff --git a/pictures/LG_AKB73315611.jpg b/pictures/LG_AKB73315611.jpg new file mode 100644 index 000000000..b907ae609 Binary files /dev/null and b/pictures/LG_AKB73315611.jpg differ diff --git a/pictures/LG_AKB75415316.jpg b/pictures/LG_AKB75415316.jpg new file mode 100644 index 000000000..83541bf83 Binary files /dev/null and b/pictures/LG_AKB75415316.jpg differ diff --git a/pictures/SloeberDefineSymbols.png b/pictures/SloeberDefineSymbols.png new file mode 100644 index 000000000..10b99acf7 Binary files /dev/null and b/pictures/SloeberDefineSymbols.png differ diff --git a/readmdFrench.md b/readmdFrench.md deleted file mode 100644 index e531e9442..000000000 --- a/readmdFrench.md +++ /dev/null @@ -1,97 +0,0 @@ -## IRremote Library - - -Cette bibliothĆØque vous permet d'envoyer et de recevoir des signaux infrarouges sur un Arduino. -Des tutoriels et plus d'informations seront disponibles sur la page d'accueil officielle. - -## Version - 2.2.3 - -## Installation -1. Allez Ć  la [Releases](https://github.com/z3t0/Arduino-IRremote/releases) page. -2. TĆ©lĆ©chargez la derniĆØre version. -3. Extraire le fichier zip -4. DĆ©placez le dossier "IRremote" vers vos bibliothĆØques. -5. Assurez-vous de supprimer Arduino_Root / libraries / RobotIRremote. Où Arduino_Root fait rĆ©fĆ©rence au rĆ©pertoire d'installation d'Arduino. La bibliothĆØque RobotIRremote a des dĆ©finitions similaires Ć  IRremote et provoque des erreurs. - - -## FAQ -Je ne travaille pas correctement en utilisant Neopixels (aka WS2811 / WS2812 / WS2812B) -Que vous utilisiez la librairie Adafruit Neopixel ou FastLED, les interruptions sont dĆ©sactivĆ©es sur de nombreux processeurs bas de gamme comme les arduinos de base. ƀ son tour, cela empĆŖche le gestionnaire IR de s'exĆ©cuter quand il le faut. Il y a quelques solutions Ć  ce processus, voir cette page de Marc MERLIN -[cette page de Marc MERLIN](http://marc.merlins.org/perso/arduino/post_2017-04-03_Arduino-328P-Uno-Teensy3_1-ESP8266-ESP32-IR-and-Neopixels.html) - - -## Conseils pris en charge - -- Teensy 1.0 / 1.0++ / 2.0 / 2++ / 3.0 / 3.1 / Teensy-LC; CrĆ©dits: @PaulStoffregen (Teensy Team) -- Sanguino -- ATmega8, 48, 88, 168, 328 -- ATmega8535, 16, 32, 164, 324, 644, 1284, -- ATmega64, 128 -- ATtiny 84 / 85 -- ESP32 (recevoir seulement) -- ESP8266 est basĆ© sur un ancien code qui n'est pas trĆØs rĆ©cent, mais cela fonctionne raisonnablement bien. Voir https://github.com/markszabo/IRremoteESP8266 -Sparkfun Pro Micro - - - - -Nous sommes ouverts aux suggestions d'ajout de support pour les nouveaux tableaux, cependant, nous vous recommandons fortement de contacter votre fournisseur et de fournir un soutien de leur cĆ“tĆ©. - - -## SpĆ©cifications matĆ©rielles - - -| Carte/CPU | Envoyer Pin | Compteurs | -|--------------------------------------------------------------------------|---------------------|-------------------| -| [ATtiny84](https://github.com/SpenceKonde/ATTinyCore) | **6** | **1** | -| [ATtiny85](https://github.com/SpenceKonde/ATTinyCore) | **1** | **TINY0** | -| [ATmega8](https://github.com/MCUdude/MiniCore) | **9** | **1** | -| Atmega32u4 | 5, 9, **13** | 1, 3, **4** | -| [ATmega48, ATmega88, ATmega168, ATmega328](https://github.com/MCUdude/MiniCore) | **3**, 9 | 1, **2** | -| [ATmega1284](https://github.com/MCUdude/MightyCore) | 13, 14, 6 | 1, **2**, 3 | -| [ATmega164, ATmega324, ATmega644](https://github.com/MCUdude/MightyCore) | 13, **14** | 1, **2** | -| [ATmega8535 ATmega16, ATmega32](https://github.com/MCUdude/MightyCore) | **13** | **1** | -| [ATmega64, ATmega128](https://github.com/MCUdude/MegaCore) | **13** | **1** | -| ATmega1280, ATmega2560 | 5, 6, **9**, 11, 46 | 1, **2**, 3, 4, 5 | -| [ESP32](http://esp32.net/) | N/A (insupportĆ©) | **1** | -| [Sparkfun Pro Micro](https://www.sparkfun.com/products/12640) | 9, **5**, 5 | 1, **3**, 4_HS | -| [Teensy 1.0](https://www.pjrc.com/teensy/) | **17** | **1** | -| [Teensy 2.0](https://www.pjrc.com/teensy/) | 9, **10**, 14 | 1, 3, **4_HS** | -| [Teensy++ 1.0 / 2.0](https://www.pjrc.com/teensy/) | **1**, 16, 25 | 1, **2**, 3 | -| [Teensy 3.0 / 3.1](https://www.pjrc.com/teensy/) | **5** | **CMT** | -| [Teensy-LC](https://www.pjrc.com/teensy/) | **16** | **TPM1** | - - -## Patchs expĆ©rimentaux - -Voici les correctifs strictement pris en charge qui n'ont pas encore Ć©tĆ© intĆ©grĆ©s. Si vous avez des questions, n'hĆ©sitez pas Ć  demander ici. Si cela fonctionne, faites le nous savoir! - -[Arduino 101](https://github.com/z3t0/Arduino-IRremote/pull/481#issuecomment-311243146) - -Le tableau ci-dessus rĆ©pertorie les temporisations actuellement supportĆ©es et les broches d'envoi correspondantes, beaucoup de ces broches supplĆ©mentaires sont ouvertes. - - -## Utilisation -- ƀ faire TODO (VĆ©rifier les exemples pour l'instant) - - -## Contribution -Si vous voulez contribuer Ć  ce projet: -- Signaler les bogues et les erreurs -- Demander des amĆ©liorations -- CrĆ©er des problĆØmes et tirer des requĆŖtes -- Parlez de cette bibliothĆØque Ć  d'autres personnes -- Contribuer de nouveaux protocoles -VĆ©rifiez ici [ici](Contributing.md) pour quelques guidelines - - -## Contact -Email: zetoslab@gmail.com -Please only email me if it is more appropriate than creating an Issue / PR. I **will** not respond to requests for adding support for particular boards, unless of course you are the creator of the board and would like to cooperate on the project. I will also **ignore** any emails asking me to tell you how to implement your ideas. However, if you have a private inquiry that you would only apply to you and you would prefer it to be via email, by all means. - -## Contributeurs -Check [here](Contributors.md) -@Lsuperman735 French translation - -## Copyright -Copyright 2009-2012 Ken Shirriff diff --git a/src/IRFeedbackLED.hpp b/src/IRFeedbackLED.hpp new file mode 100644 index 000000000..0db0c02f8 --- /dev/null +++ b/src/IRFeedbackLED.hpp @@ -0,0 +1,161 @@ +/** + * @file IRFeedbackLED.hpp + * + * @brief All Feedback LED specific functions are contained in this file. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************* + * MIT License + * + * Copyright (c) 2021-2022 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_FEEDBACK_LED_HPP +#define _IR_FEEDBACK_LED_HPP + +/** \addtogroup FeedbackLEDFunctions Feedback LED functions + * @{ + */ + +/** + * Contains pin number and enable status of the feedback LED + */ +struct FeedbackLEDControlStruct { + uint8_t FeedbackLEDPin; ///< if 0, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions + uint8_t LedFeedbackEnabled; ///< LED_FEEDBACK_ENABLED_FOR_RECEIVE or LED_FEEDBACK_ENABLED_FOR_SEND -> enable blinking of pin on IR processing +}; + +struct FeedbackLEDControlStruct FeedbackLEDControl; ///< The feedback LED control instance + +/** + * Enables blinking of feedback LED (LED_BUILTIN is taken as default) on IR sending and receiving + * Cannot disable it here!!! Use disableLEDFeedbackForReceive() or disableLEDFeedbackForSend() + * @param aFeedbackLEDPin If aFeedbackLEDPin == 0, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions + * If FeedbackLEDPin == 0 and no LED_BUILTIN defined, disable LED feedback + * @param aEnableLEDFeedback If LED_FEEDBACK_ENABLED_FOR_RECEIVE or LED_FEEDBACK_ENABLED_FOR_SEND -> enable blinking of Feedback LED + */ +void setLEDFeedback(uint8_t aFeedbackLEDPin, uint8_t aEnableLEDFeedback) { + + FeedbackLEDControl.FeedbackLEDPin = aFeedbackLEDPin; // default is 0 -> use LED_BUILTIN if available, else disable feedback + + if (aEnableLEDFeedback != DO_NOT_ENABLE_LED_FEEDBACK) { + FeedbackLEDControl.LedFeedbackEnabled |= aEnableLEDFeedback; + if (aFeedbackLEDPin != USE_DEFAULT_FEEDBACK_LED_PIN) { + pinModeFast(aFeedbackLEDPin, OUTPUT); +#if defined(LED_BUILTIN) + } else { + pinModeFast(LED_BUILTIN, OUTPUT); +#else + FeedbackLEDControl.LedFeedbackEnabled = LED_FEEDBACK_DISABLED_COMPLETELY; // we have no LED_BUILTIN available +#endif + } + } +} + +/* + * Direct replacement for blink13() + */ +void setLEDFeedback(bool aEnableLEDFeedback) { + bool tEnableLEDFeedback = LED_FEEDBACK_DISABLED_COMPLETELY; + if (aEnableLEDFeedback) { + tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_SEND | LED_FEEDBACK_ENABLED_FOR_RECEIVE; + } + setLEDFeedback(FeedbackLEDControl.FeedbackLEDPin, tEnableLEDFeedback); +} + +void enableLEDFeedback() { + FeedbackLEDControl.LedFeedbackEnabled |= LED_FEEDBACK_ENABLED_FOR_RECEIVE; +} + +void disableLEDFeedback() { + FeedbackLEDControl.LedFeedbackEnabled &= ~(LED_FEEDBACK_ENABLED_FOR_RECEIVE); +} + +void enableLEDFeedbackForSend() { + FeedbackLEDControl.LedFeedbackEnabled |= LED_FEEDBACK_ENABLED_FOR_SEND; +} + +void disableLEDFeedbackForSend() { + FeedbackLEDControl.LedFeedbackEnabled &= ~(LED_FEEDBACK_ENABLED_FOR_SEND); +} + +/** + * Flash LED while receiving or sending IR data. Does not check if enabled, this must be done by the caller. + * Handles the 0 value of FeedbackLEDPin and the macro FEEDBACK_LED_IS_ACTIVE_LOW. + */ +#if defined(ESP32) || defined(ESP8266) +IRAM_ATTR +#endif +void setFeedbackLED(bool aSwitchLedOn) { + if (aSwitchLedOn) { + if (FeedbackLEDControl.FeedbackLEDPin != USE_DEFAULT_FEEDBACK_LED_PIN) { + // Turn user defined pin LED on +#if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + if (__builtin_constant_p(FeedbackLEDControl.FeedbackLEDPin) ) { digitalWriteFast(FeedbackLEDControl.FeedbackLEDPin, LOW);} else { digitalWrite(FeedbackLEDControl.FeedbackLEDPin, LOW);} +#else + if (__builtin_constant_p(FeedbackLEDControl.FeedbackLEDPin) ) { digitalWriteFast(FeedbackLEDControl.FeedbackLEDPin, HIGH);} else { digitalWrite(FeedbackLEDControl.FeedbackLEDPin, HIGH);} +#endif +#if defined(LED_BUILTIN) // use fast macros here + } else { +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(LED_BUILTIN, LOW); // For AVR, this generates a single cbi command +# else + digitalWriteFast(LED_BUILTIN, HIGH); // For AVR, this generates a single sbi command +# endif +#endif + } + } else { + if (FeedbackLEDControl.FeedbackLEDPin != USE_DEFAULT_FEEDBACK_LED_PIN) { + // Turn user defined pin LED off +#if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + if (__builtin_constant_p(FeedbackLEDControl.FeedbackLEDPin) ) { digitalWriteFast(FeedbackLEDControl.FeedbackLEDPin, HIGH);} else { digitalWrite(FeedbackLEDControl.FeedbackLEDPin, HIGH);} +#else + if (__builtin_constant_p(FeedbackLEDControl.FeedbackLEDPin) ) { digitalWriteFast(FeedbackLEDControl.FeedbackLEDPin, LOW);} else { digitalWrite(FeedbackLEDControl.FeedbackLEDPin, LOW);} +#endif +#if defined(LED_BUILTIN) + } else { +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(LED_BUILTIN, HIGH); // For AVR, this generates a single sbi command +# else + digitalWriteFast(LED_BUILTIN, LOW); // For AVR, this generates a single cbi command +# endif +#endif + } + } +} + +/** + * Old deprecated function name for setLEDFeedback() or enableLEDFeedback() / disableLEDFeedback() + */ +void IRrecv::blink13(uint8_t aEnableLEDFeedback) { + setLEDFeedback(FeedbackLEDControl.FeedbackLEDPin, aEnableLEDFeedback); +} +/** + * Old deprecated function name for setLEDFeedback() + */ +void setBlinkPin(uint8_t aBlinkPin) { + setLEDFeedback(aBlinkPin, FeedbackLEDControl.LedFeedbackEnabled); +} + +/** @}*/ + +#endif // _IR_FEEDBACK_LED_HPP diff --git a/src/IRProtocol.h b/src/IRProtocol.h new file mode 100644 index 000000000..2d7654767 --- /dev/null +++ b/src/IRProtocol.h @@ -0,0 +1,183 @@ +/** + * @file IRProtocol.h + * @brief Common declarations for receiving and sending. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_PROTOCOL_H +#define _IR_PROTOCOL_H + +/** + * An enum consisting of all supported formats. + * You do NOT need to remove entries from this list when disabling protocols! + * !!!Must be the same order as ProtocolNames in IRReceive.hpp!!! + */ +typedef enum { + UNKNOWN = 0, + PULSE_WIDTH, + PULSE_DISTANCE, + APPLE, + DENON, + JVC, + LG, + LG2, + NEC, + NEC2, /* 10 NEC with full frame as repeat */ + ONKYO, + PANASONIC, + KASEIKYO, + KASEIKYO_DENON, + KASEIKYO_SHARP, + KASEIKYO_JVC, + KASEIKYO_MITSUBISHI, + RC5, + RC6, + RC6A, /*31 bit + 3 fixed 0b110 mode bits*/ + SAMSUNG, /* 20*/ + SAMSUNGLG, + SAMSUNG48, + SHARP, + SONY, + /* Now the exotic protocols */ + BANG_OLUFSEN, + BOSEWAVE, + LEGO_PF, + MAGIQUEST, + WHYNTER, /* 30 */ + FAST +} decode_type_t; +extern const char *const ProtocolNames[]; // The array of name strings for the decode_type_t enum + +#define SIRCS_12_PROTOCOL 12 +#define SIRCS_15_PROTOCOL 15 +#define SIRCS_20_PROTOCOL 20 + +struct DistanceWidthTimingInfoStruct { + uint16_t HeaderMarkMicros; + uint16_t HeaderSpaceMicros; + uint16_t OneMarkMicros; + uint16_t OneSpaceMicros; + uint16_t ZeroMarkMicros; + uint16_t ZeroSpaceMicros; +}; + +/* + * Definitions for member IRData.flags + */ +#define IRDATA_FLAGS_EMPTY 0x00 +#define IRDATA_FLAGS_IS_REPEAT 0x01 ///< The gap between the preceding frame is as smaller than the maximum gap expected for a repeat. !!!We do not check for changed command or address, because it is almost not possible to press 2 different buttons on the remote within around 100 ms!!! +#define IRDATA_FLAGS_IS_AUTO_REPEAT 0x02 ///< The current repeat frame is a repeat, that is always sent after a regular frame and cannot be avoided. Only specified for protocols DENON, and LEGO. +#define IRDATA_FLAGS_PARITY_FAILED 0x04 ///< The current (autorepeat) frame violated parity check. +#define IRDATA_FLAGS_TOGGLE_BIT 0x08 ///< Is set if RC5 or RC6 toggle bit is set. +#define IRDATA_TOGGLE_BIT_MASK 0x08 ///< deprecated -is set if RC5 or RC6 toggle bit is set. +#define IRDATA_FLAGS_EXTRA_INFO 0x10 ///< There is extra info not contained in address and data (e.g. Kaseikyo unknown vendor ID, or in decodedRawDataArray). +#define IRDATA_FLAGS_IS_PROTOCOL_WITH_DIFFERENT_REPEAT 0x20 ///< Here we have a repeat of type NEC2 or SamsungLG +#define IRDATA_FLAGS_WAS_OVERFLOW 0x40 ///< irparams.rawlen is set to 0 in this case to avoid endless OverflowFlag. +#define IRDATA_FLAGS_IS_MSB_FIRST 0x80 ///< Value is mainly determined by the (known) protocol. +#define IRDATA_FLAGS_IS_LSB_FIRST 0x00 + +#define DECODED_RAW_DATA_ARRAY_SIZE ((((RAW_BUFFER_LENGTH - 2) - 1) / (2 * BITS_IN_RAW_DATA_TYPE)) + 1) // The -2 is for initial gap + stop bit mark, 128 mark + spaces for 64 bit. +/** + * Data structure for the user application, available as decodedIRData. + * Filled by decoders and read by print functions or user application. + */ +struct IRData { + decode_type_t protocol; ///< UNKNOWN, NEC, SONY, RC5, PULSE_DISTANCE, ... + uint16_t address; ///< Decoded address, Distance protocol (tMarkTicksLong (if tMarkTicksLong == 0, then tMarkTicksShort) << 8) | tSpaceTicksLong + uint16_t command; ///< Decoded command, Distance protocol (tMarkTicksShort << 8) | tSpaceTicksShort + uint16_t extra; ///< Contains upper 16 bit of Magiquest WandID, Kaseikyo unknown vendor ID and Distance protocol (HeaderMarkTicks << 8) | HeaderSpaceTicks. + IRRawDataType decodedRawData; ///< Up to 32/64 bit decoded raw data, to be used for send functions. +#if defined(DECODE_DISTANCE_WIDTH) + // This replaces the address, command, extra and decodedRawData in case of protocol == PULSE_DISTANCE or -rather seldom- protocol == PULSE_WIDTH. + DistanceWidthTimingInfoStruct DistanceWidthTimingInfo; // 12 bytes + IRRawDataType decodedRawDataArray[DECODED_RAW_DATA_ARRAY_SIZE]; ///< 32/64 bit decoded raw data, to be used for send function. +#endif + uint16_t numberOfBits; ///< Number of bits received for data (address + command + parity) - to determine protocol length if different length are possible. + uint8_t flags; ///< IRDATA_FLAGS_IS_REPEAT, IRDATA_FLAGS_WAS_OVERFLOW etc. See IRDATA_FLAGS_* definitions above + + /* + * These 2 variables allow to call resume() directly after decode. + * After resume(), decodedIRData.rawDataPtr->initialGapTicks and decodedIRData.rawDataPtr->rawlen are + * the first variables, which are overwritten by the next received frame. + * since 4.3.0. + */ + IRRawlenType rawlen; ///< counter of entries in rawbuf of last received frame. + uint16_t initialGapTicks; ///< contains the initial gap (pre 4.4: the value in rawbuf[0]) of the last received frame. + + irparams_struct *rawDataPtr; ///< Pointer of the raw timing data to be decoded. Mainly the OverflowFlag and the data buffer filled by receiving ISR. +}; + +extern uint8_t sLastSendToggleValue; // Currently used by RC5 + RC6 + +struct PulseDistanceWidthProtocolConstants { + decode_type_t ProtocolIndex; + uint_fast8_t FrequencyKHz; + DistanceWidthTimingInfoStruct DistanceWidthTimingInfo; + uint8_t Flags; + unsigned int RepeatPeriodMillis; + void (*SpecialSendRepeatFunction)(); // using non member functions here saves up to 250 bytes for send demo +// void (IRsend::*SpecialSendRepeatFunction)(); +}; +/* + * Definitions for member PulseDistanceWidthProtocolConstants.Flags + */ +#define SUPPRESS_STOP_BIT 0x20 // Stop bit is otherwise sent for all pulse distance protocols, i.e. aOneSpaceMicros != aZeroSpaceMicros. +#define PROTOCOL_IS_MSB_FIRST IRDATA_FLAGS_IS_MSB_FIRST +#define PROTOCOL_IS_LSB_FIRST IRDATA_FLAGS_IS_LSB_FIRST + +/* + * Carrier frequencies for various protocols + */ +#if !defined(BEO_KHZ) // guard used for unit test, which sends and receive Bang&Olufsen with 38 kHz. +#define BEO_KHZ 455 +#endif +#define SONY_KHZ 40 +#define BOSEWAVE_KHZ 38 +#define DENON_KHZ 38 +#define JVC_KHZ 38 +#define LG_KHZ 38 +#define NEC_KHZ 38 +#define SAMSUNG_KHZ 38 +#define KASEIKYO_KHZ 37 +#define RC5_RC6_KHZ 36 + +#if defined(__AVR__) +const __FlashStringHelper* getProtocolString(decode_type_t aProtocol); +#else +const char* getProtocolString(decode_type_t aProtocol); +#endif +void printIRResultShort(Print *aSerial, IRData *aIRDataPtr, bool aPrintRepeatGap) __attribute__ ((deprecated ("Remove last parameter, it is not supported any more."))); +void printIRResultShort(Print *aSerial, IRData *aIRDataPtr); // A static function to be able to print send or copied received data. + +/* + * Convenience functions to convert MSB to LSB values + */ +uint8_t bitreverseOneByte(uint8_t aValue); +uint32_t bitreverse32Bit(uint32_t aInput); + +#endif // _IR_PROTOCOL_H diff --git a/src/IRProtocol.hpp b/src/IRProtocol.hpp new file mode 100644 index 000000000..bed8fa7a6 --- /dev/null +++ b/src/IRProtocol.hpp @@ -0,0 +1,309 @@ +/* + * IRReceive.hpp + * This file is exclusively included by IRremote.h to enable easy configuration of library switches + * + * Contains all protocol functions used by receiver and sender. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2009-2023 Ken Shirriff, Rafi Khan, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_PROTOCOL_HPP +#define _IR_PROTOCOL_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Receiving Receiving IR data for multiple protocols + * @{ + */ + +const char string_Unknown[] PROGMEM = "UNKNOWN"; +const char string_PulseWidth[] PROGMEM = "PulseWidth"; +const char string_PulseDistance[] PROGMEM = "PulseDistance"; +const char string_Apple[] PROGMEM = "Apple"; +const char string_Denon[] PROGMEM = "Denon"; +const char string_JVC[] PROGMEM = "JVC"; +const char string_LG[] PROGMEM = "LG"; +const char string_LG2[] PROGMEM = "LG2"; +const char string_NEC[] PROGMEM = "NEC"; +const char string_NEC2[] PROGMEM = "NEC2"; +const char string_Onkyo[] PROGMEM = "Onkyo"; +const char string_Panasonic[] PROGMEM = "Panasonic"; +const char string_Kaseikyo[] PROGMEM = "Kaseikyo"; +const char string_Kaseikyo_Denon[] PROGMEM = "Kaseikyo_Denon"; +const char string_Kaseikyo_Sharp[] PROGMEM = "Kaseikyo_Sharp"; +const char string_Kaseikyo_JVC[] PROGMEM = "Kaseikyo_JVC"; +const char string_Kaseikyo_Mitsubishi[] PROGMEM = "Kaseikyo_Mitsubishi"; +const char string_RC5[] PROGMEM = "RC5"; +const char string_RC6[] PROGMEM = "RC6"; +const char string_RC6A[] PROGMEM = "RC6A"; +const char string_Samsung[] PROGMEM = "Samsung"; +const char string_SamsungLG[] PROGMEM = "SamsungLG"; +const char string_Samsung48[] PROGMEM = "Samsung48"; +const char string_Sharp[] PROGMEM = "Sharp"; +const char string_Sony[] PROGMEM = "Sony"; +const char string_BangOlufsen[] PROGMEM = "Bang&Olufsen"; +const char string_BoseWave[] PROGMEM = "BoseWave"; +const char string_Lego[] PROGMEM = "Lego"; +const char string_MagiQuest[] PROGMEM = "MagiQuest"; +const char string_Whynter[] PROGMEM = "Whynter"; +const char string_FAST[] PROGMEM = "FAST"; + +/* + * !!Must be the same order as in decode_type_t in IRProtocol.h!!! + */ +const char *const ProtocolNames[] +PROGMEM = { string_Unknown, string_PulseWidth, string_PulseDistance, string_Apple, string_Denon, string_JVC, string_LG, string_LG2, + string_NEC, string_NEC2, string_Onkyo, string_Panasonic, string_Kaseikyo, string_Kaseikyo_Denon, string_Kaseikyo_Sharp, + string_Kaseikyo_JVC, string_Kaseikyo_Mitsubishi, string_RC5, string_RC6, string_RC6A, string_Samsung, string_SamsungLG, + string_Samsung48, string_Sharp, string_Sony +#if !defined(EXCLUDE_EXOTIC_PROTOCOLS) + , string_BangOlufsen, string_BoseWave, string_Lego, string_MagiQuest, string_Whynter, string_FAST +#endif + }; + +#if defined(__AVR__) +const __FlashStringHelper* getProtocolString(decode_type_t aProtocol) { + const char *tProtocolStringPtr = (char*) pgm_read_word(&ProtocolNames[aProtocol]); + return ((__FlashStringHelper*) (tProtocolStringPtr)); +} +#else +const char* getProtocolString(decode_type_t aProtocol) { + return ProtocolNames[aProtocol]; +} +#endif + +#if (__INT_WIDTH__ >= 32) +# if __has_include() +/* + * This code to handle the missing print(unsigned long long...) function of seeduino core was contributed by sklott + * https://stackoverflow.com/questions/74622227/avoid-calling-of-function-size-t-printprintunsigned-long-long-n-int-base-if + */ +#include + +// If you have C++17 you can just use std::void_t, or use this for all versions +#if __cpp_lib_void_t >= 201411L +template +using void_t = std::void_t; +#else +template struct make_void { + typedef void type; +}; +template using void_t = typename make_void::type; +#endif + +// Detecting if we have print(unsigned long long value, int base) / print(0ull, 0) overload +template +struct has_ull_print: std::false_type { +}; +template +struct has_ull_print().print(0ull, 0))>> : std::true_type { +}; + +// Must be namespace, to avoid public and static declarations for class +namespace PrintULL { +template::value, bool>::type = true> +size_t print(PrintImplType *p, unsigned long long value, int base) { + size_t tLength = p->print(static_cast(value >> 32), base); + tLength += p->print(static_cast(value), base); + return tLength; +} + +template::value, bool>::type = true> +size_t print(PrintImplType *p, unsigned long long value, int base) { + return p->print(value, base); +} +} +; +# else +namespace PrintULL { + size_t print(Print *aSerial, unsigned long long n, int base) { + return aSerial->print(n, base); + } +}; +# endif +#endif + +/** @} + * \addtogroup Print Print functions + * @{ + */ +/** + * Function to print decoded result and flags in one line. + * A static function to be able to print data to send or copied received data. + * Ends with println(). + * + * @param aSerial The Print object on which to write, for Arduino you can use &Serial. + * @param aIRDataPtr Pointer to the data to be printed. + * @param aPrintRepeatGap If true also print the gap before repeats. + * + */ +void printIRResultShort(Print *aSerial, IRData *aIRDataPtr, bool aPrintRepeatGap) { + (void) aPrintRepeatGap; + printIRResultShort(aSerial, aIRDataPtr); +} +void printIRResultShort(Print *aSerial, IRData *aIRDataPtr) { + if (aIRDataPtr->flags & IRDATA_FLAGS_WAS_OVERFLOW) { + aSerial->println(F("Overflow")); + return; + } + aSerial->print(F("Protocol=")); + aSerial->print(getProtocolString(aIRDataPtr->protocol)); + if (aIRDataPtr->protocol == UNKNOWN) { +#if defined(DECODE_HASH) + aSerial->print(F(" Hash=0x")); +#if (__INT_WIDTH__ < 32) + aSerial->print(aIRDataPtr->decodedRawData, HEX); +#else + PrintULL::print(aSerial,aIRDataPtr->decodedRawData, HEX); +#endif + +#endif +#if !defined(DISABLE_CODE_FOR_RECEIVER) + aSerial->print(' '); + aSerial->print((aIRDataPtr->rawlen + 1) / 2, DEC); + aSerial->println(F(" bits (incl. gap and start) received")); +#endif + } else { +#if defined(DECODE_DISTANCE_WIDTH) + if (aIRDataPtr->protocol != PULSE_DISTANCE && aIRDataPtr->protocol != PULSE_WIDTH) { +#endif + /* + * New decoders have address and command + */ + aSerial->print(F(" Address=0x")); + aSerial->print(aIRDataPtr->address, HEX); + + aSerial->print(F(" Command=0x")); + aSerial->print(aIRDataPtr->command, HEX); + + if (aIRDataPtr->flags & IRDATA_FLAGS_EXTRA_INFO) { + aSerial->print(F(" Extra=0x")); + aSerial->print(aIRDataPtr->extra, HEX); + } + + if (aIRDataPtr->flags & IRDATA_FLAGS_PARITY_FAILED) { + aSerial->print(F(" Parity fail")); + } + + if (aIRDataPtr->flags & IRDATA_FLAGS_TOGGLE_BIT) { + aSerial->print(F(" Toggle=1")); + } +#if defined(DECODE_DISTANCE_WIDTH) + } +#endif + + /* + * Print raw data + */ + if (!(aIRDataPtr->flags & IRDATA_FLAGS_IS_REPEAT) || aIRDataPtr->decodedRawData != 0) { + aSerial->print(F(" Raw-Data=0x")); +#if (__INT_WIDTH__ < 32) + aSerial->print(aIRDataPtr->decodedRawData, HEX); +#else + PrintULL::print(aSerial, aIRDataPtr->decodedRawData, HEX); +#endif + /* + * Print number of bits processed + */ + aSerial->print(' '); + aSerial->print(aIRDataPtr->numberOfBits, DEC); + aSerial->print(F(" bits")); + + if (aIRDataPtr->flags & IRDATA_FLAGS_IS_MSB_FIRST) { + aSerial->print(F(" MSB first")); + } else { + aSerial->print(F(" LSB first")); + } + } + + /* + * Print gap and duration, in order to be able to compute the repeat period of the protocol by adding the next gap time + */ + if (aIRDataPtr->flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) { + aSerial->print(' '); + if (aIRDataPtr->flags & IRDATA_FLAGS_IS_AUTO_REPEAT) { + aSerial->print(F("Auto-")); + } + aSerial->print(F("Repeat")); + } +#if !defined(DISABLE_CODE_FOR_RECEIVER) + aSerial->print(F(" Gap=")); + aSerial->print((uint32_t) aIRDataPtr->initialGapTicks * MICROS_PER_TICK); + aSerial->print(F("us")); + + uint16_t tSumOfDurationTicks = 0; + for (IRRawlenType i = 1; i < aIRDataPtr->rawlen; i++) { + tSumOfDurationTicks += aIRDataPtr->rawDataPtr->rawbuf[i]; + } + aSerial->print(F(" Duration=")); + aSerial->print((uint32_t) tSumOfDurationTicks * MICROS_PER_TICK, DEC); + aSerial->println(F("us")); +#else + aSerial->println(); +#endif + } +} + +/** @} + * \addtogroup Utils Utility functions + * @{ + */ +/********************************************************************************************************************** + * Function to bit reverse OLD MSB values of e.g. NEC. + **********************************************************************************************************************/ +uint8_t bitreverseOneByte(uint8_t aValue) { +// uint8_t tReversedValue; +// return __builtin_avr_insert_bits(0x01234567, aValue, tReversedValue); +// 76543210 + aValue = (aValue >> 4) | (aValue << 4); // Swap in groups of 4 +// 32107654 + aValue = ((aValue & 0xcc) >> 2) | ((aValue & 0x33) << 2); // Swap in groups of 2 +// 10325476 + aValue = ((aValue & 0xaa) >> 1) | ((aValue & 0x55) << 1); // Swap bit pairs +// 01234567 + return aValue; +} + +uint32_t bitreverse32Bit(uint32_t aInput) { +// __builtin_avr_insert_bits(); + LongUnion tValue; + tValue.UByte.HighByte = bitreverseOneByte(aInput); + tValue.UByte.MidHighByte = bitreverseOneByte(aInput >> 8); + tValue.UByte.MidLowByte = bitreverseOneByte(aInput >> 16); + tValue.UByte.LowByte = bitreverseOneByte(aInput >> 24); + return tValue.ULong; +} + +/** @}*/ + +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_PROTOCOL_HPP diff --git a/src/IRReceive.hpp b/src/IRReceive.hpp new file mode 100644 index 000000000..1c6ab4fb5 --- /dev/null +++ b/src/IRReceive.hpp @@ -0,0 +1,2040 @@ +/* + * IRReceive.hpp + * This file is exclusively included by IRremote.h to enable easy configuration of library switches + * + * Contains all IRrecv class functions as well as other receiver related functions. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2009-2023 Ken Shirriff, Rafi Khan, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_RECEIVE_HPP +#define _IR_RECEIVE_HPP + +#if defined(DEBUG) +//#define LOCAL_DEBUG // +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +#if defined(TRACE) && !defined(LOCAL_TRACE) +#define LOCAL_TRACE +#else +//#define LOCAL_TRACE // This enables debug output only for this file +#endif +/* + * Low level hardware timing measurement + */ +//#define _IR_MEASURE_TIMING // for ISR +//#define _IR_TIMING_TEST_PIN 7 // "pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);" is executed at start() +// +/** \addtogroup Receiving Receiving IR data for multiple protocols + * @{ + */ +/** + * The receiver instance + */ +IRrecv IrReceiver; + +/* + * The control structure instance + */ +struct irparams_struct irparams; // the irparams instance +unsigned long sMicrosAtLastStopTimer = 0; // Used to adjust TickCounterForISR with uncounted ticks between stopTimer() and restartTimer() + +/** + * Instantiate the IRrecv class. Multiple instantiation is not supported. + * @param IRReceivePin Arduino pin to use. No sanity check is made. + */ +IRrecv::IRrecv() { + decodedIRData.rawDataPtr = &irparams; // for decodePulseDistanceData() etc. + setReceivePin(0); +#if !defined(NO_LED_RECEIVE_FEEDBACK_CODE) + setLEDFeedback(0, DO_NOT_ENABLE_LED_FEEDBACK); +#endif +} + +IRrecv::IRrecv(uint_fast8_t aReceivePin) { + decodedIRData.rawDataPtr = &irparams; // for decodePulseDistanceData() etc. + setReceivePin(aReceivePin); +#if !defined(NO_LED_RECEIVE_FEEDBACK_CODE) + setLEDFeedback(0, DO_NOT_ENABLE_LED_FEEDBACK); +#endif +} + +/** + * Instantiate the IRrecv class. Multiple instantiation is not supported. + * @param aReceivePin Arduino pin to use, where a demodulating IR receiver is connected. + * @param aFeedbackLEDPin if 0, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions + */ +IRrecv::IRrecv(uint_fast8_t aReceivePin, uint_fast8_t aFeedbackLEDPin) { + decodedIRData.rawDataPtr = &irparams; // for decodePulseDistanceData() etc. + setReceivePin(aReceivePin); +#if !defined(NO_LED_RECEIVE_FEEDBACK_CODE) + setLEDFeedback(aFeedbackLEDPin, DO_NOT_ENABLE_LED_FEEDBACK); +#else + (void) aFeedbackLEDPin; +#endif +} + +/********************************************************************************************************************** + * Interrupt Service Routine - Called every 50 us + * + * Duration in ticks of 50 us of alternating SPACE, MARK are recorded in irparams.rawbuf array. + * 'rawlen' counts the number of entries recorded so far. + * First entry is the SPACE between transmissions. + * + * As soon as one SPACE entry gets longer than RECORD_GAP_TICKS, state switches to STOP (frame received). Timing of SPACE continues. + * A call of resume() switches from STOP to IDLE. + * As soon as first MARK arrives in IDLE, gap width is recorded and new logging starts. + * + * With digitalRead and Feedback LED + * 15 pushs, 1 in, 1 eor before start of code = 2 us @16MHz + * 7.2 us computation time (6us idle time) + * pop + reti = 2.25 us @16MHz => 10.3 to 11.5 us @16MHz + * With portInputRegister and mask and Feedback LED code commented + * 9 pushs, 1 in, 1 eor before start of code = 1.25 us @16MHz + * 2.25 us computation time + * pop + reti = 1.5 us @16MHz => 5 us @16MHz + * => Minimal CPU frequency is 4 MHz + * + **********************************************************************************************************************/ +#if defined(ESP8266) || defined(ESP32) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wvolatile" +IRAM_ATTR +#endif +void IRReceiveTimerInterruptHandler() { +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) + digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles +#endif +// 7 - 8.5 us for ISR body (without pushes and pops) for ATmega328 @16MHz + +#if defined(TIMER_REQUIRES_RESET_INTR_PENDING) + timerResetInterruptPending(); // reset TickCounterForISR interrupt flag if required (currently only for Teensy and ATmega4809) +#endif + +// Read if IR Receiver -> SPACE [xmt LED off] or a MARK [xmt LED on] +#if defined(__AVR__) + uint8_t tIRInputLevel = *irparams.IRReceivePinPortInputRegister & irparams.IRReceivePinMask; +#else + uint_fast8_t tIRInputLevel = (uint_fast8_t) digitalReadFast(irparams.IRReceivePin); +#endif + + /* + * Increase TickCounter and clip it at maximum 0xFFFF / 3.2 seconds at 50 us ticks + */ + if (irparams.TickCounterForISR < UINT16_MAX) { + irparams.TickCounterForISR++; // One more 50uS tick + } + + /* + * Due to a ESP32 compiler bug https://github.com/espressif/esp-idf/issues/1552 no switch statements are possible for ESP32 + * So we change the code to if / else if + */ +// switch (irparams.StateForISR) { +// + if (irparams.StateForISR == IR_REC_STATE_IDLE) { + /* + * Here we are just resumed and maybe in the middle of a transmission + */ + if (tIRInputLevel == INPUT_MARK) { + // check if we did not start in the middle of a transmission by checking the minimum length of leading space + if (irparams.TickCounterForISR > RECORD_GAP_TICKS) { +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) +// digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles +#endif + /* + * Gap between two transmissions just ended; Record gap duration + start recording transmission + * Initialize all state machine variables + */ + irparams.OverflowFlag = false; + // irparams.rawbuf[0] = irparams.TickCounterForISR; + // Usage of initialGapTicks enables usage of 8 bit buffer instead of 16 bit since 4.4, + // because the big gap value is not stored in this buffer any more + irparams.initialGapTicks = irparams.TickCounterForISR; + irparams.rawlen = 1; + irparams.StateForISR = IR_REC_STATE_MARK; + } // otherwise stay in idle state + irparams.TickCounterForISR = 0; // reset counter in both cases + } + + } else if (irparams.StateForISR == IR_REC_STATE_MARK) { + // Timing mark here, rawlen is even + if (tIRInputLevel != INPUT_MARK) { + /* + * Mark ended here. Record mark time in rawbuf array + */ +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) +// digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles +#endif + irparams.rawbuf[irparams.rawlen++] = irparams.TickCounterForISR; // record mark + irparams.StateForISR = IR_REC_STATE_SPACE; + irparams.TickCounterForISR = 0; // This resets the tick counter also at end of frame :-) + } + + } else if (irparams.StateForISR == IR_REC_STATE_SPACE) { + /* + * In space receiving here, rawlen is odd + * Check for timeout or overflow + */ + if (irparams.TickCounterForISR > RECORD_GAP_TICKS || irparams.rawlen >= RAW_BUFFER_LENGTH - 1) { + if (irparams.rawlen >= RAW_BUFFER_LENGTH) { + // Flag up a read OverflowFlag; Stop the state machine + irparams.OverflowFlag = true; + } + /* + * Overflow or maximum space duration reached here. + * Current code is ready for processing! + * We received a long space, which indicates gap between codes. + * Switch to IR_REC_STATE_STOP + * Don't reset TickCounterForISR; keep counting width of next leading space + */ + /* + * These 2 variables allow to call resume() directly after decode. + * After resume(), decodedIRData.rawDataPtr->initialGapTicks and decodedIRData.rawDataPtr->rawlen are + * the first variables, which are overwritten by the next received frame. + * since 4.3.0. + * For backward compatibility, there are the same 2 statements in decode() if IrReceiver is not used. + */ + IrReceiver.decodedIRData.initialGapTicks = irparams.initialGapTicks; + IrReceiver.decodedIRData.rawlen = irparams.rawlen; + + irparams.StateForISR = IR_REC_STATE_STOP; // This signals the decode(), that a complete frame was received +#if !defined(IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK) + /* + * Call callback if registered (not nullptr) + */ + if (irparams.ReceiveCompleteCallbackFunction != nullptr) { + irparams.ReceiveCompleteCallbackFunction(); + } +#endif + } else if (tIRInputLevel == INPUT_MARK) { + /* + * Space ended here. + */ + +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) +// digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles +#endif + irparams.rawbuf[irparams.rawlen++] = irparams.TickCounterForISR; // record space + irparams.StateForISR = IR_REC_STATE_MARK; + irparams.TickCounterForISR = 0; + } + } else if (irparams.StateForISR == IR_REC_STATE_STOP) { + /* + * Complete command received + * stay here until resume() is called, which switches state to IR_REC_STATE_IDLE + */ +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) +// digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles +#endif + if (tIRInputLevel == INPUT_MARK) { + // Reset gap TickCounterForISR, to prepare for detection if we are in the middle of a transmission after call of resume() + irparams.TickCounterForISR = 0; + } + } + +#if !defined(NO_LED_RECEIVE_FEEDBACK_CODE) + if (FeedbackLEDControl.LedFeedbackEnabled & LED_FEEDBACK_ENABLED_FOR_RECEIVE) { + setFeedbackLED(tIRInputLevel == INPUT_MARK); + } +#endif + +#ifdef _IR_MEASURE_TIMING + digitalWriteFast(_IR_TIMING_TEST_PIN, LOW); // 2 clock cycles +#endif + +} + +/* + * The ISR, which calls the interrupt handler + */ +#if defined(TIMER_INTR_NAME) || defined(ISR) +# if defined(TIMER_INTR_NAME) +ISR (TIMER_INTR_NAME) // for ISR definitions +# elif defined(ISR) +ISR() +// for functions definitions which are called by separate (board specific) ISR +# endif +{ + IRReceiveTimerInterruptHandler(); +} +#endif + +/********************************************************************************************************************** + * Stream like API + **********************************************************************************************************************/ + +/** + * Initializes the receive and feedback pin + * @param aReceivePin The Arduino pin number, where a demodulating IR receiver is connected. + * @param aEnableLEDFeedback if true / ENABLE_LED_FEEDBACK, then let the feedback led blink on receiving IR signal + * @param aFeedbackLEDPin if 0 / USE_DEFAULT_FEEDBACK_LED_PIN, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions + */ +void IRrecv::begin(uint_fast8_t aReceivePin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) { + + setReceivePin(aReceivePin); +#if !defined(NO_LED_RECEIVE_FEEDBACK_CODE) + uint_fast8_t tEnableLEDFeedback = DO_NOT_ENABLE_LED_FEEDBACK; + if (aEnableLEDFeedback) { + tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_RECEIVE; + } + setLEDFeedback(aFeedbackLEDPin, tEnableLEDFeedback); +#else + (void) aEnableLEDFeedback; + (void) aFeedbackLEDPin; +#endif + +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) + pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT); +#endif + start(); +} + +/** + * Sets / changes the receiver pin number + */ +void IRrecv::setReceivePin(uint_fast8_t aReceivePinNumber) { + irparams.IRReceivePin = aReceivePinNumber; +#if defined(__AVR__) +# if defined(__digitalPinToBit) + if (__builtin_constant_p(aReceivePinNumber)) { + irparams.IRReceivePinMask = 1UL << (__digitalPinToBit(aReceivePinNumber)); + } else { + irparams.IRReceivePinMask = digitalPinToBitMask(aReceivePinNumber); // requires 10 bytes PGM, even if not referenced (?because it is assembler code?) + } +# else + irparams.IRReceivePinMask = digitalPinToBitMask(aReceivePinNumber); // requires 10 bytes PGM, even if not referenced (?because it is assembler code?) +# endif +# if defined(__digitalPinToPINReg) + /* + * This code is 54 bytes smaller, if aReceivePinNumber is a constant :-), but 38 byte longer if it is not constant (,which is not likely). + */ + if (__builtin_constant_p(aReceivePinNumber)) { + irparams.IRReceivePinPortInputRegister = __digitalPinToPINReg(aReceivePinNumber); + } else { + irparams.IRReceivePinPortInputRegister = portInputRegister(digitalPinToPort(aReceivePinNumber)); // requires 44 bytes PGM, even if not referenced + } +# else + irparams.IRReceivePinPortInputRegister = portInputRegister(digitalPinToPort(aReceivePinNumber)); // requires 44 bytes PGM, even if not referenced +# endif +#endif + // Seems to be at least required by ESP32 + // Set pin mode once. pinModeFast makes no difference if used, but saves 224 if not referenced :-( + if (__builtin_constant_p(aReceivePinNumber)) { + pinModeFast(aReceivePinNumber, INPUT); + } else { + pinModeFast(aReceivePinNumber, INPUT); + } +} + +#if !defined(IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK) +/** + * Sets the function to call if a complete protocol frame has arrived + */ +void IRrecv::registerReceiveCompleteCallback(void (*aReceiveCompleteCallbackFunction)(void)) { + irparams.ReceiveCompleteCallbackFunction = aReceiveCompleteCallbackFunction; +} +#endif + +/** + * Start the receiving process. + * This configures the timer and the state machine for IR reception + * and enables the receive sample timer interrupt which consumes a small amount of CPU every 50 us. + */ +void IRrecv::start() { + + // Setup for cyclic 50 us interrupt + timerConfigForReceive(); // no interrupts enabled here! + + // Initialize state machine state + resume(); + + // Timer interrupt is enabled after state machine reset + timerEnableReceiveInterrupt(); // Enables the receive sample timer interrupt which consumes a small amount of CPU every 50 us. +#ifdef _IR_MEASURE_TIMING + pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT); +#endif +} + +/* + * Restarts timer interrupts, adjusts TickCounterForISR for correct gap value after stopTimer(). Does not call resume()! + */ +void IRrecv::restartTimer() { + // Setup for cyclic 50 us interrupt + timerConfigForReceive(); // no interrupts enabled here! + // Timer interrupt is enabled after state machine reset + if (sMicrosAtLastStopTimer != 0) { + irparams.TickCounterForISR += (micros() - sMicrosAtLastStopTimer) / MICROS_PER_TICK; // adjust TickCounterForISR for correct gap value, which is used for repeat detection + sMicrosAtLastStopTimer = 0; + } + timerEnableReceiveInterrupt(); // Enables the receive sample timer interrupt which consumes a small amount of CPU every 50 us. +#ifdef _IR_MEASURE_TIMING + pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT); +#endif +} +/** + * Alias for start(). + */ +void IRrecv::enableIRIn() { + start(); +} + +/** + * Configures the timer and the state machine for IR reception. Does not call resume()! + * We assume, that timer interrupts are disabled here, otherwise it makes no sense to use this functions. + * Therefore we do not need to guard the change of the volatile TickCounterForISR here :-). + * The tick counter value is already at 100 when decode() gets true, because of the 5000 us minimal gap defined in RECORD_GAP_MICROS. + * If TickCounterForISR is not adjusted with the value of the microseconds, the timer was stopped, + * it can happen, that a new IR frame is recognized as a repeat, because the value of RECORD_GAP_MICROS + * was not reached by TickCounterForISR counter before receiving the new IR frame. + * @param aMicrosecondsToAddToGapCounter To compensate for the amount of microseconds the timer was stopped / disabled. + */ +void IRrecv::restartTimer(uint32_t aMicrosecondsToAddToGapCounter) { + irparams.TickCounterForISR += aMicrosecondsToAddToGapCounter / MICROS_PER_TICK; + timerConfigForReceive(); // no interrupts enabled here! + timerEnableReceiveInterrupt(); // Enables the receive sample timer interrupt which consumes a small amount of CPU every 50 us. +#ifdef _IR_MEASURE_TIMING + pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT); +#endif +} +/** + * Configures the timer and the state machine for IR reception. Does not call resume()! + * @param aTicksToAddToGapCounter To compensate for the amount of ticks the timer was stopped / disabled. + */ +void IRrecv::restartTimerWithTicksToAdd(uint16_t aTicksToAddToGapCounter) { + irparams.TickCounterForISR += aTicksToAddToGapCounter; + timerConfigForReceive(); // no interrupts enabled here! + timerEnableReceiveInterrupt(); // Enables the receive sample timer interrupt which consumes a small amount of CPU every 50 us. +#ifdef _IR_MEASURE_TIMING + pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT); +#endif +} +#if defined(ESP8266) || defined(ESP32) +#pragma GCC diagnostic push +#endif + +/** + * Restarts receiver after send. Is a NOP if sending does not require a timer. + */ +void IRrecv::restartAfterSend() { +#if defined(SEND_PWM_BY_TIMER) && !defined(SEND_PWM_DOES_NOT_USE_RECEIVE_TIMER) + start(); +#endif +} + +/** + * Disables the timer for IR reception. + */ +void IRrecv::stop() { + timerDisableReceiveInterrupt(); +} + +/* + * Stores microseconds of stop, to adjust TickCounterForISR in restartTimer() + */ +void IRrecv::stopTimer() { + timerDisableReceiveInterrupt(); + sMicrosAtLastStopTimer = micros(); +} +/** + * Alias for stop(). + */ +void IRrecv::disableIRIn() { + stop(); +} +/** + * Alias for stop(). + */ +void IRrecv::end() { + stop(); +} + +/** + * Returns status of reception + * @return true if no reception is on-going. + */ +bool IRrecv::isIdle() { + return (irparams.StateForISR == IR_REC_STATE_IDLE || irparams.StateForISR == IR_REC_STATE_STOP) ? true : false; +} + +/** + * Restart the ISR (Interrupt Service Routine) state machine, to enable receiving of the next IR frame. + * Internal counting of gap timing is independent of StateForISR and therefore independent of call time of resume(). + */ +void IRrecv::resume() { + // This check allows to call resume at arbitrary places or more than once + if (irparams.StateForISR == IR_REC_STATE_STOP) { + irparams.StateForISR = IR_REC_STATE_IDLE; + } +} + +/** + * Is internally called by decode before calling decoders. + * Must be used to setup data, if you call decoders manually. + */ +void IRrecv::initDecodedIRData() { + + if (irparams.OverflowFlag) { + decodedIRData.flags = IRDATA_FLAGS_WAS_OVERFLOW; +#if defined(LOCAL_DEBUG) + Serial.print(F("Overflow happened, try to increase the \"RAW_BUFFER_LENGTH\" value of ")); + Serial.print(RAW_BUFFER_LENGTH); + Serial.println(F(" with #define RAW_BUFFER_LENGTH=")); +#endif + + } else { + decodedIRData.flags = IRDATA_FLAGS_EMPTY; + // save last protocol, command and address for repeat handling (where they are compared or copied back :-)) + lastDecodedProtocol = decodedIRData.protocol; // repeat patterns can be equal between protocols (e.g. NEC, Samsung and LG), so we must keep the original one + lastDecodedCommand = decodedIRData.command; + lastDecodedAddress = decodedIRData.address; + + } + decodedIRData.protocol = UNKNOWN; + decodedIRData.command = 0; + decodedIRData.address = 0; + decodedIRData.decodedRawData = 0; + decodedIRData.numberOfBits = 0; +} + +/** + * Returns true if IR receiver data is available. + */ +bool IRrecv::available() { + return (irparams.StateForISR == IR_REC_STATE_STOP); +} + +/** + * Returns pointer to IrReceiver.decodedIRData if IR receiver data is available, else nullptr. + */ +IRData* IRrecv::read() { + if (irparams.StateForISR != IR_REC_STATE_STOP) { + return nullptr; + } + if (decode()) { + return &decodedIRData; + } else { + return nullptr; + } +} + +/** + * The main decode function, attempts to decode the recently receive IR signal. + * The set of decoders used is determined by active definitions of the DECODE_ macros. + * Results of decoding are stored in IrReceiver.decodedIRData.* like e.g. IrReceiver.decodedIRData.command. + * @return false if no IR receiver data available, true if data available. + */ +bool IRrecv::decode() { + if (irparams.StateForISR != IR_REC_STATE_STOP) { + return false; + } + + /* + * Support for old examples, which do not use the default IrReceiver instance + */ + if (this != &IrReceiver) { + decodedIRData.initialGapTicks = irparams.initialGapTicks; + decodedIRData.rawlen = irparams.rawlen; + } + + initDecodedIRData(); // sets IRDATA_FLAGS_WAS_OVERFLOW + + if (decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) { + /* + * Set OverflowFlag flag and return true here, to let the loop call resume or print raw data. + */ + decodedIRData.protocol = UNKNOWN; + return true; + } + +#if defined(DECODE_NEC) || defined(DECODE_ONKYO) + IR_TRACE_PRINTLN(F("Attempting NEC/Onkyo decode")); + if (decodeNEC()) { + return true; + } +#endif + +#if defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO) + IR_TRACE_PRINTLN(F("Attempting Panasonic/Kaseikyo decode")); + if (decodeKaseikyo()) { + return true; + } +#endif + +#if defined(DECODE_DENON) + IR_TRACE_PRINTLN(F("Attempting Denon/Sharp decode")); + if (decodeDenon()) { + return true; + } +#endif + +#if defined(DECODE_SONY) + IR_TRACE_PRINTLN(F("Attempting Sony decode")); + if (decodeSony()) { + return true; + } +#endif + +#if defined(DECODE_RC5) + IR_TRACE_PRINTLN(F("Attempting RC5 decode")); + if (decodeRC5()) { + return true; + } +#endif + +#if defined(DECODE_RC6) + IR_TRACE_PRINTLN(F("Attempting RC6 decode")); + if (decodeRC6()) { + return true; + } +#endif + +#if defined(DECODE_LG) + IR_TRACE_PRINTLN(F("Attempting LG decode")); + if (decodeLG()) { + return true; + } +#endif + +#if defined(DECODE_JVC) + IR_TRACE_PRINTLN(F("Attempting JVC decode")); + if (decodeJVC()) { + return true; + } +#endif + +#if defined(DECODE_SAMSUNG) + IR_TRACE_PRINTLN(F("Attempting Samsung decode")); + if (decodeSamsung()) { + return true; + } +#endif + /* + * Start of the exotic protocols + */ + +#if defined(DECODE_BEO) + IR_TRACE_PRINTLN(F("Attempting Bang & Olufsen decode")); + if (decodeBangOlufsen()) { + return true; + } +#endif + +#if defined(DECODE_FAST) + IR_TRACE_PRINTLN(F("Attempting FAST decode")); + if (decodeFAST()) { + return true; + } +#endif + +#if defined(DECODE_WHYNTER) + IR_TRACE_PRINTLN(F("Attempting Whynter decode")); + if (decodeWhynter()) { + return true; + } +#endif + +#if defined(DECODE_LEGO_PF) + IR_TRACE_PRINTLN(F("Attempting Lego Power Functions")); + if (decodeLegoPowerFunctions()) { + return true; + } +#endif + +#if defined(DECODE_BOSEWAVE) + IR_TRACE_PRINTLN(F("Attempting Bosewave decode")); + if (decodeBoseWave()) { + return true; + } +#endif + +#if defined(DECODE_MAGIQUEST) + IR_TRACE_PRINTLN(F("Attempting MagiQuest decode")); + if (decodeMagiQuest()) { + return true; + } +#endif + + /* + * Try the universal decoder for pulse distance protocols + */ +#if defined(DECODE_DISTANCE_WIDTH) + IR_TRACE_PRINTLN(F("Attempting universal Distance Width decode")); + if (decodeDistanceWidth()) { + return true; + } +#endif + + /* + * Last resort is the universal hash decode which always return true + */ +#if defined(DECODE_HASH) + IR_TRACE_PRINTLN(F("Hash decode")); + // decodeHash returns a hash on any input. + // Thus, it needs to be last in the list. + // If you add any decodes, add them before this. + if (decodeHash()) { + return true; + } +#endif + + /* + * Return true here, to let the loop decide to call resume or to print raw data. + */ + return true; +} + +/********************************************************************************************************************** + * Common decode functions + **********************************************************************************************************************/ +/** + * Decode pulse distance width protocols. We only check the mark or space length of a 1, otherwise we always assume a 0! + * + * We can have the following protocol timings + * PULSE_DISTANCE: Pause/spaces have different length and determine the bit value, longer space is 1. Pulses/marks can be constant, like NEC. + * PULSE_WIDTH: Pulses/marks have different length and determine the bit value, longer mark is 1. Pause/spaces can be constant, like Sony. + * PULSE_DISTANCE_WIDTH: Pulses/marks and pause/spaces have different length, often the bit length is constant, like MagiQuest. Can be decoded by PULSE_DISTANCE decoder. + * + * Input is IrReceiver.decodedIRData.rawDataPtr->rawbuf[] + * Output is IrReceiver.decodedIRData.decodedRawData + * + * Assume PULSE_DISTANCE if aOneMarkMicros == aZeroMarkMicros + * + * @param aNumberOfBits Number of bits to decode from decodedIRData.rawDataPtr->rawbuf[] array. + * @param aStartOffset Offset in decodedIRData.rawDataPtr->rawbuf[] to start decoding. Must point to a mark. + * @param aOneMarkMicros Checked if PULSE_WIDTH + * @param aZeroMarkMicros Required for deciding if we have PULSE_DISTANCE. + * @param aOneSpaceMicros Checked if PULSE_DISTANCE. + * @param aMSBfirst If true send Most Significant Bit first, else send Least Significant Bit (lowest bit) first. + * @return true If decoding was successful + */ +bool IRrecv::decodePulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros, + uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, bool aMSBfirst) { + + auto *tRawBufPointer = &decodedIRData.rawDataPtr->rawbuf[aStartOffset]; + + bool isPulseDistanceProtocol = (aOneMarkMicros == aZeroMarkMicros); // If true, we check aOneSpaceMicros -> pulse distance protocol + + IRRawDataType tDecodedData = 0; // For MSB first tDecodedData is shifted left each loop + IRRawDataType tMask = 1UL; // Mask is only used for LSB first + + for (uint_fast8_t i = aNumberOfBits; i > 0; i--) { + // get one mark and space pair + unsigned int tMarkTicks; + unsigned int tSpaceTicks; + bool tBitValue; + + if (isPulseDistanceProtocol) { + /* + * PULSE_DISTANCE -including PULSE_DISTANCE_WIDTH- here. + * !!!We only check variable length space indicating a 1 or 0!!! + */ + tRawBufPointer++; + tSpaceTicks = *tRawBufPointer++; // maybe buffer overflow for last bit, but we do not evaluate this value :-) + tBitValue = matchSpace(tSpaceTicks, aOneSpaceMicros); // Check for variable length space indicating a 1 or 0 + + } else { + /* + * PULSE_WIDTH here. + * !!!We only check variable length mark indicating a 1 or 0!!! + */ + tMarkTicks = *tRawBufPointer++; + tBitValue = matchMark(tMarkTicks, aOneMarkMicros); // Check for variable length mark indicating a 1 or 0 + tRawBufPointer++; + } + + if (aMSBfirst) { + tDecodedData <<= 1; + } + + if (tBitValue) { + // It's a 1 -> set the bit + if (aMSBfirst) { + tDecodedData |= 1; + } else { + tDecodedData |= tMask; + } + IR_TRACE_PRINTLN(F("=> 1")); + } else { + // do not set the bit + IR_TRACE_PRINTLN(F("=> 0")); + } + tMask <<= 1; + } + decodedIRData.decodedRawData = tDecodedData; + return true; +} + +/* + * Old deprecated version with 7 parameters and unused aZeroSpaceMicros parameter + */ +bool IRrecv::decodePulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros, + uint16_t aZeroMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroSpaceMicros, bool aMSBfirst) { + + (void) aZeroSpaceMicros; + auto *tRawBufPointer = &decodedIRData.rawDataPtr->rawbuf[aStartOffset]; + bool isPulseDistanceProtocol = (aOneMarkMicros == aZeroMarkMicros); // If true, we have a constant mark -> pulse distance protocol + + IRRawDataType tDecodedData = 0; // For MSB first tDecodedData is shifted left each loop + IRRawDataType tMask = 1UL; // Mask is only used for LSB first + + for (uint_fast8_t i = aNumberOfBits; i > 0; i--) { + // get one mark and space pair + unsigned int tMarkTicks; + unsigned int tSpaceTicks; + bool tBitValue; + + if (isPulseDistanceProtocol) { + /* + * Pulse distance here, it is not required to check constant mark duration (aOneMarkMicros) and zero space duration. + */ + + (void) aZeroSpaceMicros; + tRawBufPointer++; + tSpaceTicks = *tRawBufPointer++; // maybe buffer overflow for last bit, but we do not evaluate this value :-) + tBitValue = matchSpace(tSpaceTicks, aOneSpaceMicros); // Check for variable length space indicating a 1 or 0 + } else { + /* + * Pulse width here, it is not required to check (constant) space duration and zero mark duration. + */ + tMarkTicks = *tRawBufPointer++; + tBitValue = matchMark(tMarkTicks, aOneMarkMicros); // Check for variable length mark indicating a 1 or 0 + tRawBufPointer++; + } + + if (aMSBfirst) { + tDecodedData <<= 1; + } + + if (tBitValue) { + // It's a 1 -> set the bit + if (aMSBfirst) { + tDecodedData |= 1; + } else { + tDecodedData |= tMask; + } + IR_TRACE_PRINTLN(F("=> 1")); + } else { + // do not set the bit + IR_TRACE_PRINTLN(F("=> 0")); + } + tMask <<= 1; + } + decodedIRData.decodedRawData = tDecodedData; + return true; +} + +/* + * Check for additional required characteristics of timing like length of mark for a constant mark protocol, + * where space length determines the bit value. Requires up to 194 additional bytes of program memory. + * Only sensible for development or very exotic requirements. + * @param aZeroMarkMicros For strict checks + * @param aZeroSpaceMicros For strict checks + * + * Not used yet + */ +bool IRrecv::decodePulseDistanceWidthDataStrict(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros, + uint16_t aZeroMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroSpaceMicros, bool aMSBfirst) { + + auto *tRawBufPointer = &decodedIRData.rawDataPtr->rawbuf[aStartOffset]; + + bool isPulseDistanceProtocol = (aOneMarkMicros == aZeroMarkMicros); // If true, we have a constant mark -> pulse distance protocol + + IRRawDataType tDecodedData = 0; // For MSB first tDecodedData is shifted left each loop + IRRawDataType tMask = 1UL; // Mask is only used for LSB first + + for (uint_fast8_t i = aNumberOfBits; i > 0; i--) { + // get one mark and space pair + unsigned int tMarkTicks; + unsigned int tSpaceTicks; + bool tBitValue; + + if (isPulseDistanceProtocol) { + /* + * PULSE_DISTANCE here, it is not required to check constant mark duration (aOneMarkMicros) and zero space duration. + */ + tMarkTicks = *tRawBufPointer++; + tSpaceTicks = *tRawBufPointer++; // maybe buffer overflow for last bit, but we do not evaluate this value :-) + tBitValue = matchSpace(tSpaceTicks, aOneSpaceMicros); // Check for variable length space indicating a 1 or 0 + + // Check for constant length mark + if (!matchMark(tMarkTicks, aOneMarkMicros)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Mark=")); + Serial.print(tMarkTicks * MICROS_PER_TICK); + Serial.print(F(" is not ")); + Serial.print(aOneMarkMicros); + Serial.print(F(". Index=")); + Serial.print(aNumberOfBits - i); + Serial.print(' '); +#endif + return false; + } + + } else { + /* + * PULSE_DISTANCE -including PULSE_DISTANCE_WIDTH- here. + * !!!We only check variable length mark indicating a 1 or 0!!! + * It is not required to check space duration and zero mark duration. + */ + tMarkTicks = *tRawBufPointer++; + tBitValue = matchMark(tMarkTicks, aOneMarkMicros); // Check for variable length mark indicating a 1 or 0 + tSpaceTicks = *tRawBufPointer++; // maybe buffer overflow for last bit, but we do not evaluate this value :-) + } + + if (aMSBfirst) { + tDecodedData <<= 1; + } + + if (tBitValue) { + // It's a 1 -> set the bit + if (aMSBfirst) { + tDecodedData |= 1; + } else { + tDecodedData |= tMask; + } + IR_TRACE_PRINTLN(F("=> 1")); + } else { + /* + * Additionally check length of tSpaceTicks parameter for PULSE_DISTANCE or tMarkTicks for PULSE_WIDTH + * which determine a zero + */ + if (isPulseDistanceProtocol) { + if (!matchSpace(tSpaceTicks, aZeroSpaceMicros)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Space=")); + Serial.print(tSpaceTicks * MICROS_PER_TICK); + Serial.print(F(" is not ")); + Serial.print(aOneSpaceMicros); + Serial.print(F(" or ")); + Serial.print(aZeroSpaceMicros); + Serial.print(F(". Index=")); + Serial.print(aNumberOfBits - i); + Serial.print(' '); +#endif + return false; + } + } else { + if (!matchMark(tMarkTicks, aZeroMarkMicros)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Mark=")); + Serial.print(tMarkTicks * MICROS_PER_TICK); + Serial.print(F(" is not ")); + Serial.print(aOneMarkMicros); + Serial.print(F(" or ")); + Serial.print(aZeroMarkMicros); + Serial.print(F(". Index=")); + Serial.print(aNumberOfBits - i); + Serial.print(' '); +#endif + return false; + } + } + // do not set the bit + IR_TRACE_PRINTLN(F("=> 0")); + } + // If we have no stop bit, assume that last space, which is not recorded, is correct, since we can not check it + if (aZeroSpaceMicros == aOneSpaceMicros + && tRawBufPointer < &decodedIRData.rawDataPtr->rawbuf[decodedIRData.rawDataPtr->rawlen]) { + // Check for constant length space (of pulse width protocol) here + if (!matchSpace(tSpaceTicks, aOneSpaceMicros)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Space=")); + Serial.print(tSpaceTicks * MICROS_PER_TICK); + Serial.print(F(" is not ")); + Serial.print(aOneSpaceMicros); + Serial.print(F(". Index=")); + Serial.print(aNumberOfBits - i); + Serial.print(' '); +#endif + return false; + } + } + tMask <<= 1; + } + decodedIRData.decodedRawData = tDecodedData; + return true; +} + +/** + * Decode pulse distance protocols for PulseDistanceWidthProtocolConstants. + * @return true if decoding was successful + */ +bool IRrecv::decodePulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, uint_fast8_t aNumberOfBits, + IRRawlenType aStartOffset) { + + return decodePulseDistanceWidthData(aNumberOfBits, aStartOffset, aProtocolConstants->DistanceWidthTimingInfo.OneMarkMicros, + aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros, aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros, + aProtocolConstants->Flags); +} + +bool IRrecv::decodePulseDistanceWidthData_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, + uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset) { + PulseDistanceWidthProtocolConstants tTemporaryPulseDistanceWidthProtocolConstants; + memcpy_P(&tTemporaryPulseDistanceWidthProtocolConstants, aProtocolConstantsPGM, + sizeof(tTemporaryPulseDistanceWidthProtocolConstants)); + + return decodePulseDistanceWidthData(&tTemporaryPulseDistanceWidthProtocolConstants, aNumberOfBits, aStartOffset); +} + +/* + * Static variables for the getBiphaselevel function + */ +uint_fast8_t sBiphaseDecodeRawbuffOffset; // Index into raw timing array +uint16_t sBiphaseCurrentTimingIntervals; // 1, 2 or 3. Number of aBiphaseTimeUnit intervals of the current rawbuf[sBiphaseDecodeRawbuffOffset] timing. +uint_fast8_t sBiphaseUsedTimingIntervals; // Number of already used intervals of sCurrentTimingIntervals. +uint16_t sBiphaseTimeUnit; + +void IRrecv::initBiphaselevel(uint_fast8_t aRCDecodeRawbuffOffset, uint16_t aBiphaseTimeUnit) { + sBiphaseDecodeRawbuffOffset = aRCDecodeRawbuffOffset; + sBiphaseTimeUnit = aBiphaseTimeUnit; + sBiphaseUsedTimingIntervals = 0; +} + +/** + * Gets the level of one time interval (aBiphaseTimeUnit) at a time from the raw buffer. + * The RC5/6 decoding is easier if the data is broken into time intervals. + * E.g. if the buffer has mark for 2 time intervals and space for 1, + * successive calls to getBiphaselevel will return 1, 1, 0. + * + * _ _ _ _ _ _ _ _ _ _ _ _ _ + * _____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| | + * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ Significant clock edge + * _ _ _ ___ _ ___ ___ _ - Mark + * Data _____| |___| |_| |_| |_| |___| |___| |_| | - Data starts with a mark->space bit + * 1 0 0 0 1 1 0 1 0 1 1 - Space + * A mark to space at a significant clock edge results in a 1 + * A space to mark at a significant clock edge results in a 0 (for RC6) + * Returns current level [MARK or SPACE] or -1 for error (measured time interval is not a multiple of sBiphaseTimeUnit). + */ +uint_fast8_t IRrecv::getBiphaselevel() { + uint_fast8_t tLevelOfCurrentInterval; // 0 (SPACE) or 1 (MARK) + + if (sBiphaseDecodeRawbuffOffset >= decodedIRData.rawlen) { + return SPACE; // After end of recorded buffer, assume space. + } + + tLevelOfCurrentInterval = (sBiphaseDecodeRawbuffOffset) & 1; // on odd rawbuf offsets we have mark timings + + /* + * Setup data if sUsedTimingIntervals is 0 + */ + if (sBiphaseUsedTimingIntervals == 0) { + uint16_t tCurrentTimingWith = decodedIRData.rawDataPtr->rawbuf[sBiphaseDecodeRawbuffOffset]; + uint16_t tMarkExcessCorrection = (tLevelOfCurrentInterval == MARK) ? MARK_EXCESS_MICROS : -MARK_EXCESS_MICROS; + + if (matchTicks(tCurrentTimingWith, sBiphaseTimeUnit + tMarkExcessCorrection)) { + sBiphaseCurrentTimingIntervals = 1; + } else if (matchTicks(tCurrentTimingWith, (2 * sBiphaseTimeUnit) + tMarkExcessCorrection)) { + sBiphaseCurrentTimingIntervals = 2; + } else if (matchTicks(tCurrentTimingWith, (3 * sBiphaseTimeUnit) + tMarkExcessCorrection)) { + sBiphaseCurrentTimingIntervals = 3; + } else { + return -1; + } + } + +// We use another interval from tCurrentTimingIntervals + sBiphaseUsedTimingIntervals++; + +// keep track of current timing offset + if (sBiphaseUsedTimingIntervals >= sBiphaseCurrentTimingIntervals) { + // we have used all intervals of current timing, switch to next timing value + sBiphaseUsedTimingIntervals = 0; + sBiphaseDecodeRawbuffOffset++; + } + + IR_TRACE_PRINTLN(tLevelOfCurrentInterval); + + return tLevelOfCurrentInterval; +} + +/********************************************************************************************************************** + * Internal Hash decode function + **********************************************************************************************************************/ +#define FNV_PRIME_32 16777619 ///< used for decodeHash() +#define FNV_BASIS_32 2166136261 ///< used for decodeHash() + +/** + * Compare two (tick) values for Hash decoder + * Use a tolerance of 20% to enable e.g. 500 and 600 (NEC timing) to be equal + * @return 0 if newval is shorter, 1 if newval is equal, and 2 if newval is longer + */ +uint_fast8_t IRrecv::compare(uint16_t oldval, uint16_t newval) { + if (newval * 10 < oldval * 8) { + return 0; + } + if (oldval * 10 < newval * 8) { + return 2; + } + return 1; +} + +/** + * Decodes an arbitrary IR code to a 32-bit value. + * Instead of decoding using a standard encoding scheme + * (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. + * + * The algorithm looks at the sequence of MARK and SPACE signals, and see if each one + * is shorter (0), the same length (1), or longer (2) than the previous MARK or SPACE. + * It hash the resulting sequence of 0's, 1's, and 2's to a 32-bit value. + * This will give a unique value for each different code (probably), for most code systems. + * + * Uses FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param + * Converts the raw code values into a 32-bit hash code. + * Hopefully this code is unique for each button. + * This isn't a "real" decoding, just an arbitrary value. + * + * see: http://www.righto.com/2010/01/using-arbitrary-remotes-with-arduino.html + */ +bool IRrecv::decodeHash() { + unsigned long hash = FNV_BASIS_32; // the result is the same no matter if we use a long or unsigned long variable + +// Require at least 6 samples to prevent triggering on noise + if (decodedIRData.rawlen < 6) { + IR_DEBUG_PRINT(F("HASH: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is less than 6")); + return false; + } + for (IRRawlenType i = 1; (i + 2) < decodedIRData.rawlen; i++) { + // Compare mark with mark and space with space + uint_fast8_t value = compare(decodedIRData.rawDataPtr->rawbuf[i], decodedIRData.rawDataPtr->rawbuf[i + 2]); + // Add value into the hash + hash = (hash * FNV_PRIME_32) ^ value; + } + + decodedIRData.decodedRawData = hash; + decodedIRData.numberOfBits = 32; + decodedIRData.protocol = UNKNOWN; + + return true; +} + +bool IRrecv::decodeHashOld(decode_results *aResults) { + unsigned long hash = FNV_BASIS_32; + +// Require at least 6 samples to prevent triggering on noise + if (aResults->rawlen < 6) { + return false; + } + + for (uint8_t i = 3; i < aResults->rawlen; i++) { + uint_fast8_t value = compare(aResults->rawbuf[i - 2], aResults->rawbuf[i]); + // Add value into the hash + hash = (hash * FNV_PRIME_32) ^ value; + } + + aResults->value = hash; + aResults->bits = 32; + aResults->decode_type = UNKNOWN; + decodedIRData.protocol = UNKNOWN; + + return true; +} + +/********************************************************************************************************************** + * Match functions + **********************************************************************************************************************/ + +/* + * returns true if values do match + */ +bool IRrecv::checkHeader(PulseDistanceWidthProtocolConstants *aProtocolConstants) { +// Check header "mark" and "space" + if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], aProtocolConstants->DistanceWidthTimingInfo.HeaderMarkMicros)) { +#if defined(LOCAL_TRACE) + Serial.print(::getProtocolString(aProtocolConstants->ProtocolIndex)); + Serial.println(F(": Header mark length is wrong")); +#endif + return false; + } + if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], aProtocolConstants->DistanceWidthTimingInfo.HeaderSpaceMicros)) { +#if defined(LOCAL_TRACE) + Serial.print(::getProtocolString(aProtocolConstants->ProtocolIndex)); + Serial.println(F(": Header space length is wrong")); +#endif + return false; + } + return true; +} + +bool IRrecv::checkHeader_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM) { +// Check header "mark" and "space" + if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], pgm_read_word(&aProtocolConstantsPGM->DistanceWidthTimingInfo.HeaderMarkMicros))) { +#if defined(LOCAL_TRACE) + Serial.print(::getProtocolString(aProtocolConstantsPGM->ProtocolIndex)); + Serial.println(F(": Header mark length is wrong")); +#endif + return false; + } + if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], pgm_read_word(&aProtocolConstantsPGM->DistanceWidthTimingInfo.HeaderSpaceMicros))) { +#if defined(LOCAL_TRACE) + Serial.print(::getProtocolString(aProtocolConstantsPGM->ProtocolIndex)); + Serial.println(F(": Header space length is wrong")); +#endif + return false; + } + return true; +} + +/* + * Does not check for same address and command, because it is almost not possible to press 2 different buttons on the remote within around 100 ms. + * And if really required, it can be enabled here, or done manually in user program. + * And we have still no RC6 toggle bit check for detecting a second press on the same button. + */ +void IRrecv::checkForRepeatSpaceTicksAndSetFlag(uint16_t aMaximumRepeatSpaceTicks) { + if (decodedIRData.initialGapTicks < aMaximumRepeatSpaceTicks +#if defined(ENABLE_COMPLETE_REPEAT_CHECK) +// Check also for same command and address values to detect a repeat. Not sensible for standard protocols, because it is almost not possible to press 2 different buttons on the remote within around 100 ms + && decodedIRData.address == lastDecodedAddress && decodedIRData.command == lastDecodedCommand /* requires around 44 bytes program space */ +#endif + ) { + decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT; + } +} + +/** + * Match function without compensating for marks exceeded or spaces shortened by demodulator hardware + * @return true, if values match + * Currently not used + */ +bool matchTicks(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) { +#if defined(LOCAL_TRACE) + Serial.print(F("Testing: ")); + Serial.print(TICKS_LOW(aMatchValueMicros), DEC); + Serial.print(F(" <= ")); + Serial.print(aMeasuredTicks, DEC); + Serial.print(F(" <= ")); + Serial.print(TICKS_HIGH(aMatchValueMicros), DEC); +#endif + bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros)) && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros))); +#if defined(LOCAL_TRACE) + if (passed) { + Serial.println(F(" => passed")); + } else { + Serial.println(F(" => FAILED")); + } +#endif + return passed; +} + +bool MATCH(uint16_t measured_ticks, uint16_t desired_us) { + return matchTicks(measured_ticks, desired_us); +} + +/** + * Compensate for marks exceeded by demodulator hardware + * @return true, if values match + */ +bool matchMark(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) { +#if defined(LOCAL_TRACE) + Serial.print(F("Testing mark (actual vs desired): ")); + Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC); + Serial.print(F("us vs ")); + Serial.print(aMatchValueMicros, DEC); + Serial.print(F("us: ")); + Serial.print(TICKS_LOW(aMatchValueMicros + MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); + Serial.print(F(" <= ")); + Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC); + Serial.print(F(" <= ")); + Serial.print(TICKS_HIGH(aMatchValueMicros + MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); +#endif + // compensate for marks exceeded by demodulator hardware + bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros + MARK_EXCESS_MICROS)) + && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros + MARK_EXCESS_MICROS))); +#if defined(LOCAL_TRACE) + if (passed) { + Serial.println(F(" => passed")); + } else { + Serial.println(F(" => FAILED")); + } +#endif + return passed; +} + +bool MATCH_MARK(uint16_t measured_ticks, uint16_t desired_us) { + return matchMark(measured_ticks, desired_us); +} + +/** + * Compensate for spaces shortened by demodulator hardware + * @return true, if values match + */ +bool matchSpace(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) { +#if defined(LOCAL_TRACE) + Serial.print(F("Testing space (actual vs desired): ")); + Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC); + Serial.print(F("us vs ")); + Serial.print(aMatchValueMicros, DEC); + Serial.print(F("us: ")); + Serial.print(TICKS_LOW(aMatchValueMicros - MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); + Serial.print(F(" <= ")); + Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC); + Serial.print(F(" <= ")); + Serial.print(TICKS_HIGH(aMatchValueMicros - MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); +#endif + // compensate for spaces shortened by demodulator hardware + bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros - MARK_EXCESS_MICROS)) + && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros - MARK_EXCESS_MICROS))); +#if defined(LOCAL_TRACE) + if (passed) { + Serial.println(F(" => passed")); + } else { + Serial.println(F(" => FAILED")); + } +#endif + return passed; +} + +bool MATCH_SPACE(uint16_t measured_ticks, uint16_t desired_us) { + return matchSpace(measured_ticks, desired_us); +} + +/** + * Getter function for MARK_EXCESS_MICROS + */ +int getMarkExcessMicros() { + return MARK_EXCESS_MICROS; +} + +/** + * Checks if protocol is not detected and detected space between two transmissions + * is smaller than known value for protocols (Sony with around 24 ms) + * @return true, if CheckForRecordGapsMicros() has printed a message, i.e. gap < 15ms (RECORD_GAP_MICROS_WARNING_THRESHOLD) + */ +bool IRrecv::checkForRecordGapsMicros(Print *aSerial) { + /* + * Check if protocol is not detected and detected space between two transmissions + * is smaller than known value for protocols (Sony with around 24 ms) + */ + if (decodedIRData.protocol <= PULSE_DISTANCE + && decodedIRData.initialGapTicks < (RECORD_GAP_MICROS_WARNING_THRESHOLD / MICROS_PER_TICK)) { + aSerial->println(); + aSerial->print(F("Space of ")); + aSerial->print(decodedIRData.initialGapTicks * MICROS_PER_TICK); + aSerial->print(F(" us between two detected transmission is smaller than the minimal gap of ")); + aSerial->print(RECORD_GAP_MICROS_WARNING_THRESHOLD); + aSerial->println(F(" us known for implemented protocols like NEC, Sony, RC% etc..")); + aSerial->println(F("But it can be OK for some yet unsupported protocols, and especially for repeats.")); + aSerial->println(F("If you get unexpected results, try to increase the RECORD_GAP_MICROS in IRremote.h.")); + aSerial->println(); + return true; + } + return false; +} + +/********************************************************************************************************************** + * Print functions + * Since a library should not allocate the "Serial" object, all functions require a pointer to a Print object. + **********************************************************************************************************************/ +void IRrecv::printActiveIRProtocols(Print *aSerial) { +// call no class function with same name + ::printActiveIRProtocols(aSerial); +} +/* + * Prints a list of enabled protocols for this application. + * @param aSerial pointer to serial used for printing. Use "&Serial". + */ +void printActiveIRProtocols(Print *aSerial) { +#if defined(DECODE_ONKYO) + aSerial->print(F("Onkyo, ")); +#elif defined(DECODE_NEC) + aSerial->print(F("NEC/NEC2/Onkyo/Apple, ")); +#endif +#if defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO) + aSerial->print(F("Panasonic/Kaseikyo, ")); +#endif +#if defined(DECODE_DENON) + aSerial->print(F("Denon/Sharp, ")); +#endif +#if defined(DECODE_SONY) + aSerial->print(F("Sony, ")); +#endif +#if defined(DECODE_RC5) + aSerial->print(F("RC5, ")); +#endif +#if defined(DECODE_RC6) + aSerial->print(F("RC6, ")); +#endif +#if defined(DECODE_LG) + aSerial->print(F("LG, ")); +#endif +#if defined(DECODE_JVC) + aSerial->print(F("JVC, ")); +#endif +#if defined(DECODE_SAMSUNG) + aSerial->print(F("Samsung, ")); +#endif + /* + * Start of the exotic protocols + */ +#if defined(DECODE_BEO) + aSerial->print(F("Bang & Olufsen, ")); +#endif +#if defined(DECODE_FAST) + aSerial->print(F("FAST, ")); +#endif +#if defined(DECODE_WHYNTER) + aSerial->print(F("Whynter, ")); +#endif +#if defined(DECODE_LEGO_PF) + aSerial->print(F("Lego Power Functions, ")); +#endif +#if defined(DECODE_BOSEWAVE) + aSerial->print(F("Bosewave, ")); +#endif +#if defined(DECODE_MAGIQUEST) + aSerial->print(F("MagiQuest, ")); +#endif +#if defined(DECODE_DISTANCE_WIDTH) + aSerial->print(F("Universal Pulse Distance Width, ")); +#endif +#if defined(DECODE_HASH) + aSerial->print(F("Hash ")); +#endif +#if defined(NO_DECODER) // for sending raw only + (void)aSerial; // to avoid compiler warnings +#endif +} + +/** + * Function to print values and flags of IrReceiver.decodedIRData in one line. + * Ends with println(). + * + * @param aSerial The Print object on which to write, for Arduino you can use &Serial. + * @param aCheckForRecordGapsMicros If true, call CheckForRecordGapsMicros() which may do a long printout, + * which in turn may block the proper detection of repeats.* + * @return true, if CheckForRecordGapsMicros() has printed a message, i.e. gap < 15ms (RECORD_GAP_MICROS_WARNING_THRESHOLD). + */ +bool IRrecv::printIRResultShort(Print *aSerial, bool aCheckForRecordGapsMicros) { +// call no class function with same name + ::printIRResultShort(aSerial, &decodedIRData); + if (aCheckForRecordGapsMicros && decodedIRData.protocol != UNKNOWN) { + return checkForRecordGapsMicros(aSerial); + } + return false; +} + +void IRrecv::printDistanceWidthTimingInfo(Print *aSerial, DistanceWidthTimingInfoStruct *aDistanceWidthTimingInfo) { + aSerial->print(aDistanceWidthTimingInfo->HeaderMarkMicros); + aSerial->print(F(", ")); + aSerial->print(aDistanceWidthTimingInfo->HeaderSpaceMicros); + aSerial->print(F(", ")); + aSerial->print(aDistanceWidthTimingInfo->OneMarkMicros); + aSerial->print(F(", ")); + aSerial->print(aDistanceWidthTimingInfo->OneSpaceMicros); + aSerial->print(F(", ")); + aSerial->print(aDistanceWidthTimingInfo->ZeroMarkMicros); + aSerial->print(F(", ")); + aSerial->print(aDistanceWidthTimingInfo->ZeroSpaceMicros); +} + +/* + * Get maximum of mark ticks in rawDataPtr. + * Skip leading start and trailing stop bit. + */ +uint8_t IRrecv::getMaximumMarkTicksFromRawData() { + uint8_t tMaximumTick = 0; + for (IRRawlenType i = 3; i < decodedIRData.rawlen - 2; i += 2) { // Skip leading start and trailing stop bit. + auto tTick = decodedIRData.rawDataPtr->rawbuf[i]; + if (tMaximumTick < tTick) { + tMaximumTick = tTick; + } + } + return tMaximumTick; +} +uint8_t IRrecv::getMaximumSpaceTicksFromRawData() { + uint8_t tMaximumTick = 0; + for (IRRawlenType i = 4; i < decodedIRData.rawlen - 2; i += 2) { // Skip leading start and trailing stop bit. + auto tTick = decodedIRData.rawDataPtr->rawbuf[i]; + if (tMaximumTick < tTick) { + tMaximumTick = tTick; + } + } + return tMaximumTick; +} + +/* + * The optimizing compiler internally generates this function, if getMaximumMarkTicksFromRawData() and getMaximumSpaceTicksFromRawData() is used. + */ +uint8_t IRrecv::getMaximumTicksFromRawData(bool aSearchSpaceInsteadOfMark) { + uint8_t tMaximumTick = 0; + IRRawlenType i; + if (aSearchSpaceInsteadOfMark) { + i = 4; + } else { + i = 3; + } + for (; i < decodedIRData.rawlen - 2; i += 2) { // Skip leading start and trailing stop bit. + auto tTick = decodedIRData.rawDataPtr->rawbuf[i]; + if (tMaximumTick < tTick) { + tMaximumTick = tTick; + } + } + return tMaximumTick; +} + +uint32_t IRrecv::getTotalDurationOfRawData() { + uint16_t tSumOfDurationTicks = 0; + + for (IRRawlenType i = 1; i < decodedIRData.rawlen; i++) { + tSumOfDurationTicks += decodedIRData.rawDataPtr->rawbuf[i]; + } + return tSumOfDurationTicks * (uint32_t) MICROS_PER_TICK; +} + +/** + * Function to print values and flags of IrReceiver.decodedIRData in one line. + * do not print for repeats except IRDATA_FLAGS_IS_PROTOCOL_WITH_DIFFERENT_REPEAT. + * Ends with println(). + * !!!Attention: The result differs on a 8 bit or 32 bit platform!!! + * + * @param aSerial The Print object on which to write, for Arduino you can use &Serial. + */ +void IRrecv::printIRSendUsage(Print *aSerial) { + if (decodedIRData.protocol != UNKNOWN + && ((decodedIRData.flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) == 0x00 + || (decodedIRData.flags & IRDATA_FLAGS_IS_PROTOCOL_WITH_DIFFERENT_REPEAT))) { +#if defined(DECODE_DISTANCE_WIDTH) + uint_fast8_t tNumberOfArrayData = 0; + if (decodedIRData.protocol == PULSE_DISTANCE || decodedIRData.protocol == PULSE_WIDTH) { +# if __INT_WIDTH__ < 32 + aSerial->print(F("Send on a 8 bit platform with: ")); + tNumberOfArrayData = ((decodedIRData.numberOfBits - 1) / 32) + 1; + if(tNumberOfArrayData > 1) { + aSerial->println(); + aSerial->print(F(" uint32_t tRawData[]={0x")); +# else + aSerial->print(F("Send on a 32 bit platform with: ")); + tNumberOfArrayData = ((decodedIRData.numberOfBits - 1) / 64) + 1; + if(tNumberOfArrayData > 1) { + aSerial->println(); + aSerial->print(F(" uint64_t tRawData[]={0x")); +# endif + for (uint_fast8_t i = 0; i < tNumberOfArrayData; ++i) { +# if (__INT_WIDTH__ < 32) + aSerial->print(decodedIRData.decodedRawDataArray[i], HEX); +# else + PrintULL::print(aSerial, decodedIRData.decodedRawDataArray[i], HEX); +# endif + if (i != tNumberOfArrayData - 1) { + aSerial->print(F(", 0x")); + } + } + aSerial->println(F("};")); + aSerial->print(F(" ")); + } + } else { + aSerial->print(F("Send with: ")); + } + aSerial->print(F("IrSender.send")); + +#else + aSerial->print(F("Send with: IrSender.send")); +#endif + +#if defined(DECODE_DISTANCE_WIDTH) + if (decodedIRData.protocol != PULSE_DISTANCE && decodedIRData.protocol != PULSE_WIDTH) { +#endif + aSerial->print(getProtocolString()); + aSerial->print(F("(0x")); +#if defined(DECODE_MAGIQUEST) + if (decodedIRData.protocol == MAGIQUEST) { +# if (__INT_WIDTH__ < 32) + aSerial->print(decodedIRData.decodedRawData, HEX); +# else + PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX); +# endif + } else { + aSerial->print(decodedIRData.address, HEX); + } +#else + /* + * New decoders have address and command + */ + aSerial->print(decodedIRData.address, HEX); +#endif + + aSerial->print(F(", 0x")); + aSerial->print(decodedIRData.command, HEX); + if (decodedIRData.protocol == SONY) { + aSerial->print(F(", 2, ")); + aSerial->print(decodedIRData.numberOfBits); + } else { + aSerial->print(F(", ")); + } + +#if defined(DECODE_DISTANCE_WIDTH) + } else { + /* + * Pulse distance or pulse width here + */ + aSerial->print(F("PulseDistanceWidth")); + if(tNumberOfArrayData > 1) { + aSerial->print(F("FromArray(38, ")); + } else { + aSerial->print(F("(38, ")); + } + printDistanceWidthTimingInfo(aSerial, &decodedIRData.DistanceWidthTimingInfo); + + if(tNumberOfArrayData > 1) { + aSerial->print(F(", &tRawData[0], ")); + } else { + aSerial->print(F(", 0x")); +# if (__INT_WIDTH__ < 32) + aSerial->print(decodedIRData.decodedRawData, HEX); +# else + PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX); +# endif + aSerial->print(F(", ")); + } + aSerial->print(decodedIRData.numberOfBits);// aNumberOfBits + aSerial->print(F(", PROTOCOL_IS_")); + + if (decodedIRData.flags & IRDATA_FLAGS_IS_MSB_FIRST) { + aSerial->print('M'); + } else { + aSerial->print('L'); + } + aSerial->print(F("SB_FIRST, , ")); + } +#endif +#if defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO) || defined(DECODE_RC6) + if ((decodedIRData.flags & IRDATA_FLAGS_EXTRA_INFO) && (decodedIRData.protocol == KASEIKYO || decodedIRData.protocol == RC6A)) { + aSerial->print(F(", 0x")); + aSerial->print(decodedIRData.extra, HEX); + } +#endif + aSerial->print(F(");")); + aSerial->println(); + } +} + +/** + * Function to print protocol number, address, command, raw data and repeat flag of IrReceiver.decodedIRData in one short line. + * Does not print a Newline / does not end with println(). + * + * @param aSerial The Print object on which to write, for Arduino you can use &Serial. + */ +void IRrecv::printIRResultMinimal(Print *aSerial) { + aSerial->print(F("P=")); + aSerial->print(decodedIRData.protocol); + if (decodedIRData.protocol == UNKNOWN) { +#if defined(DECODE_HASH) + aSerial->print(F(" #=0x")); +# if (__INT_WIDTH__ < 32) + aSerial->print(decodedIRData.decodedRawData, HEX); +# else + PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX); +# endif +#endif + aSerial->print(' '); + aSerial->print((decodedIRData.rawlen + 1) / 2, DEC); + aSerial->println(F(" bits received")); + } else { + /* + * New decoders have address and command + */ + aSerial->print(F(" A=0x")); + aSerial->print(decodedIRData.address, HEX); + + aSerial->print(F(" C=0x")); + aSerial->print(decodedIRData.command, HEX); + + aSerial->print(F(" Raw=0x")); +#if (__INT_WIDTH__ < 32) + aSerial->print(decodedIRData.decodedRawData, HEX); +#else + PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX); +#endif + + if (decodedIRData.flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) { + aSerial->print(F(" R")); + } + } +} + +/* + * Not used yet + */ +void IRrecv::printIRDuration(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks) { + uint16_t tSumOfDurationTicks = 0; + for (IRRawlenType i = 1; i < decodedIRData.rawlen; i++) { + tSumOfDurationTicks += decodedIRData.rawDataPtr->rawbuf[i]; + } + aSerial->print(F("Duration=")); + if (aOutputMicrosecondsInsteadOfTicks) { + aSerial->print((uint32_t) tSumOfDurationTicks * MICROS_PER_TICK, DEC); + aSerial->println(F("us")); + + } else { + aSerial->print(tSumOfDurationTicks, DEC); + aSerial->println(F(" ticks")); + } +} + +/** + * Dump out the timings in IrReceiver.decodedIRData.rawDataPtr->rawbuf[] array 8 values per line. + * + * @param aSerial The Print object on which to write, for Arduino you can use &Serial. + * @param aOutputMicrosecondsInsteadOfTicks Output the (rawbuf_values * MICROS_PER_TICK) for better readability. + */ +void IRrecv::printIRResultRawFormatted(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks) { + +// Print Raw data + aSerial->print(F("rawData[")); + aSerial->print(decodedIRData.rawlen, DEC); + aSerial->println(F("]: ")); + + /* + * Print initial gap + */ + aSerial->print(F(" -")); + if (aOutputMicrosecondsInsteadOfTicks) { + aSerial->println((uint32_t) decodedIRData.initialGapTicks * MICROS_PER_TICK, DEC); + } else { + aSerial->println(decodedIRData.initialGapTicks, DEC); + } + +// Newline is printed every 8. value, if tCounterForNewline % 8 == 0 + uint_fast8_t tCounterForNewline = 6; // first newline is after the 2 values of the start bit + +// check if we have a protocol with no or 8 start bits +#if defined(DECODE_DENON) || defined(DECODE_MAGIQUEST) + if ( +# if defined(DECODE_DENON) + decodedIRData.protocol == DENON || decodedIRData.protocol == SHARP || +# endif +# if defined(DECODE_MAGIQUEST) + decodedIRData.protocol == MAGIQUEST || +# endif + false) { + tCounterForNewline = 0; // no or 8 start bits + } +#endif + + uint32_t tDuration; + uint16_t tSumOfDurationTicks = 0; + for (IRRawlenType i = 1; i < decodedIRData.rawlen; i++) { + auto tCurrentTicks = decodedIRData.rawDataPtr->rawbuf[i]; + if (aOutputMicrosecondsInsteadOfTicks) { + tDuration = tCurrentTicks * MICROS_PER_TICK; + } else { + tDuration = tCurrentTicks; + } + tSumOfDurationTicks += tCurrentTicks; // compute length of protocol frame + + if (!(i & 1)) { // even + aSerial->print('-'); + } else { // odd + aSerial->print(F(" +")); + } + + // padding only for big values + if (aOutputMicrosecondsInsteadOfTicks && tDuration < 1000) { + aSerial->print(' '); + } + if (aOutputMicrosecondsInsteadOfTicks && tDuration < 100) { + aSerial->print(' '); + } + if (tDuration < 10) { + aSerial->print(' '); + } + aSerial->print(tDuration, DEC); + + if ((i & 1) && (i + 1) < decodedIRData.rawlen) { + aSerial->print(','); //',' not required for last one + } + + tCounterForNewline++; + if ((tCounterForNewline % 8) == 0) { + aSerial->println(); + } + } + + aSerial->println(); + aSerial->print(F("Duration=")); + if (aOutputMicrosecondsInsteadOfTicks) { + aSerial->print((uint32_t) tSumOfDurationTicks * MICROS_PER_TICK, DEC); + aSerial->println(F("us")); + + } else { + aSerial->print(tSumOfDurationTicks, DEC); + aSerial->println(F(" ticks")); + } + aSerial->println(); + +} + +/** + * Dump out the IrReceiver.decodedIRData.rawDataPtr->rawbuf[] to be used as C definition for sendRaw(). + * + * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding! + * Print ticks in 8 bit format to save space. + * Maximum is 255*50 microseconds = 12750 microseconds = 12.75 ms, which hardly ever occurs inside an IR sequence. + * Recording of IRremote anyway stops at a gap of RECORD_GAP_MICROS (5 ms). + * + * @param aSerial The Print object on which to write, for Arduino you can use &Serial. + * @param aOutputMicrosecondsInsteadOfTicks Output the (rawbuf_values * MICROS_PER_TICK) for better readability. + */ +void IRrecv::compensateAndPrintIRResultAsCArray(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks) { +// Start declaration + if (aOutputMicrosecondsInsteadOfTicks) { + aSerial->print(F("uint16_t rawData[")); // variable type, array name + } else { + aSerial->print(F("uint8_t rawTicks[")); // variable type, array name + } + + aSerial->print(decodedIRData.rawlen - 1, DEC); // array size + aSerial->print(F("] = {")); // Start declaration + +// Dump data + for (IRRawlenType i = 1; i < decodedIRData.rawlen; i++) { + uint32_t tDuration = decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK; + + if (i & 1) { + // Mark + tDuration -= MARK_EXCESS_MICROS; + } else { + tDuration += MARK_EXCESS_MICROS; + } + + if (aOutputMicrosecondsInsteadOfTicks) { + aSerial->print(tDuration); + } else { + unsigned int tTicks = (tDuration + (MICROS_PER_TICK / 2)) / MICROS_PER_TICK; + /* + * Clip to 8 bit value + */ + tTicks = (tTicks > UINT8_MAX) ? UINT8_MAX : tTicks; + aSerial->print(tTicks); + } + if (i + 1 < decodedIRData.rawlen) aSerial->print(','); // ',' not required on last one + if (!(i & 1)) aSerial->print(' '); + } + +// End declaration + aSerial->print(F("};")); // + +// Comment + aSerial->print(F(" // ")); + printIRResultShort(aSerial); + +// Newline + aSerial->println(); +} + +/** + * Store the decodedIRData to be used for sendRaw(). + * + * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding and store it in an array provided. + * + * Maximum for uint8_t is 255*50 microseconds = 12750 microseconds = 12.75 ms, which hardly ever occurs inside an IR sequence. + * Recording of IRremote anyway stops at a gap of RECORD_GAP_MICROS (5 ms). + * @param aArrayPtr Address of an array provided by the caller. + */ +void IRrecv::compensateAndStoreIRResultInArray(uint8_t *aArrayPtr) { + +// Store data, skip leading space# + IRRawlenType i; + for (i = 1; i < decodedIRData.rawlen; i++) { + uint32_t tDuration = decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK; + if (i & 1) { + // Mark + tDuration -= MARK_EXCESS_MICROS; + } else { + tDuration += MARK_EXCESS_MICROS; + } + + unsigned int tTicks = (tDuration + (MICROS_PER_TICK / 2)) / MICROS_PER_TICK; + *aArrayPtr = (tTicks > UINT8_MAX) ? UINT8_MAX : tTicks; // we store it in an 8 bit array + aArrayPtr++; + } +} + +/** + * Print results as C variables to be used for sendXXX() + * @param aSerial The Print object on which to write, for Arduino you can use &Serial. + */ +void IRrecv::printIRResultAsCVariables(Print *aSerial) { +// Now dump "known" codes + if (decodedIRData.protocol != UNKNOWN) { + + /* + * New decoders have address and command + */ + aSerial->print(F("uint16_t")); + aSerial->print(F(" address = 0x")); + aSerial->print(decodedIRData.address, HEX); + aSerial->println(';'); + + aSerial->print(F("uint16_t")); + aSerial->print(F(" command = 0x")); + aSerial->print(decodedIRData.command, HEX); + aSerial->println(';'); + + // All protocols have raw data +#if __INT_WIDTH__ < 32 + aSerial->print(F("uint32_t rawData = 0x")); +#else + aSerial->print(F("uint64_t rawData = 0x")); +#endif +#if (__INT_WIDTH__ < 32) + aSerial->print(decodedIRData.decodedRawData, HEX); +#else + PrintULL::print(aSerial, decodedIRData.decodedRawData, HEX); +#endif + aSerial->println(';'); + aSerial->println(); + } +} + +#if defined(__AVR__) +const __FlashStringHelper* IRrecv::getProtocolString() { +// call no class function with same name + return ::getProtocolString(decodedIRData.protocol); +} +#else +const char* IRrecv::getProtocolString() { + // call no class function with same name + return ::getProtocolString(decodedIRData.protocol); +} +#endif + +/********************************************************************************************************************** + * The OLD and DEPRECATED decode function with parameter aResults, kept for backward compatibility to old 2.0 tutorials + * This function calls the old MSB first decoders and fills only the 3 variables: + * aResults->value + * aResults->bits + * aResults->decode_type + **********************************************************************************************************************/ +bool IRrecv::decode_old(decode_results *aResults) { + + if (irparams.StateForISR != IR_REC_STATE_STOP) { + return false; + } + +// copy for usage by legacy programs + aResults->rawbuf[0] = irparams.initialGapTicks; + for (int i = 1; i < RAW_BUFFER_LENGTH; ++i) { + aResults->rawbuf[i] = irparams.rawbuf[i]; // copy 8 bit array into a 16 bit array + } + aResults->rawlen = irparams.rawlen; + if (irparams.OverflowFlag) { + // Copy overflow flag to decodedIRData.flags + irparams.OverflowFlag = false; + irparams.rawlen = 0; // otherwise we have OverflowFlag again at next ISR call + IR_DEBUG_PRINTLN(F("Overflow happened")); + } + aResults->overflow = irparams.OverflowFlag; + aResults->value = 0; + + decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; // for print + +#if defined(DECODE_NEC) + IR_DEBUG_PRINTLN(F("Attempting old NEC decode")); + if (decodeNECMSB(aResults)) { + return true; + } +#endif + +#if defined(DECODE_SONY) + IR_DEBUG_PRINTLN(F("Attempting old Sony decode")); + if (decodeSonyMSB(aResults)) { + return true; + } +#endif + +#if defined(DECODE_RC5) + IR_DEBUG_PRINTLN(F("Attempting RC5 decode")); + if (decodeRC5()) { + aResults->bits = decodedIRData.numberOfBits; + aResults->value = decodedIRData.decodedRawData; + aResults->decode_type = RC5; + + return true; + } +#endif + +#if defined(DECODE_RC6) + IR_DEBUG_PRINTLN(F("Attempting RC6 decode")); + if (decodeRC6()) { + aResults->bits = decodedIRData.numberOfBits; + aResults->value = decodedIRData.decodedRawData; + aResults->decode_type = RC6; + return true; + } +#endif + +// Removed bool IRrecv::decodePanasonicMSB(decode_results *aResults) since implementations was wrong (wrong length), and nobody recognized it + +#if defined(DECODE_LG) + IR_DEBUG_PRINTLN(F("Attempting old LG decode")); + if (decodeLGMSB(aResults)) {return true;} +#endif + +#if defined(DECODE_JVC) + IR_DEBUG_PRINTLN(F("Attempting old JVC decode")); + if (decodeJVCMSB(aResults)) { + return true; + } +#endif + +#if defined(DECODE_SAMSUNG) + IR_DEBUG_PRINTLN(F("Attempting old SAMSUNG decode")); + if (decodeSAMSUNG(aResults)) { + return true; + } +#endif + +#if defined(DECODE_DENON) + IR_DEBUG_PRINTLN(F("Attempting old Denon decode")); + if (decodeDenonOld(aResults)) { + return true; + } +#endif + +// decodeHash returns a hash on any input. +// Thus, it needs to be last in the list. +// If you add any decodes, add them before this. + if (decodeHashOld(aResults)) { + return true; + } +// Throw away and start over + resume(); + return false; +} + +/** @}*/ +#if defined(_IR_MEASURE_TIMING) +#undef _IR_MEASURE_TIMING +#endif +#if defined(LOCAL_TRACE) +#undef LOCAL_TRACE +#endif +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_RECEIVE_HPP diff --git a/src/IRSend.hpp b/src/IRSend.hpp new file mode 100644 index 000000000..f6c158df8 --- /dev/null +++ b/src/IRSend.hpp @@ -0,0 +1,1463 @@ +/* + * IRSend.hpp + * + * Contains common functions for sending + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2009-2023 Ken Shirriff, Rafi Khan, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_SEND_HPP +#define _IR_SEND_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +#if defined(TRACE) && !defined(LOCAL_TRACE) +#define LOCAL_TRACE +#else +//#define LOCAL_TRACE // This enables debug output only for this file +#endif + +/* + * Low level hardware timing measurement + */ +//#define _IR_MEASURE_TIMING // for mark() +//#define _IR_TIMING_TEST_PIN 7 // "pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);" is executed at begin() +// +/* + * This improves readability of code by avoiding a lot of #if defined clauses + */ +#if defined(IR_SEND_PIN) +#define sendPin IR_SEND_PIN +#endif + +/** \addtogroup Sending Sending IR data for multiple protocols + * @{ + */ + +// The sender instance +IRsend IrSender; + +IRsend::IRsend() { // @suppress("Class members should be properly initialized") +#if !defined(IR_SEND_PIN) + sendPin = 0; +#endif + +#if !defined(NO_LED_SEND_FEEDBACK_CODE) + setLEDFeedback(0, DO_NOT_ENABLE_LED_FEEDBACK); +#endif +} + +#if defined(IR_SEND_PIN) +/** + * Only required to set LED feedback + * Simple start with defaults - LED feedback enabled! Used if IR_SEND_PIN is defined. Saves program memory. + */ +void IRsend::begin(){ +# if !defined(NO_LED_SEND_FEEDBACK_CODE) + setLEDFeedback(USE_DEFAULT_FEEDBACK_LED_PIN, LED_FEEDBACK_ENABLED_FOR_SEND); +# endif +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) + pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT); +#endif +} + +/** + * Only required to set LED feedback + * @param aEnableLEDFeedback If true / ENABLE_LED_FEEDBACK, the feedback LED is activated while receiving or sending a PWM signal /a mark + * @param aFeedbackLEDPin If 0 / USE_DEFAULT_FEEDBACK_LED_PIN, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions + */ +void IRsend::begin(bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) { +#if !defined(NO_LED_SEND_FEEDBACK_CODE) + uint_fast8_t tEnableLEDFeedback = DO_NOT_ENABLE_LED_FEEDBACK; + if(aEnableLEDFeedback) { + tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_SEND; + } + setLEDFeedback(aFeedbackLEDPin, tEnableLEDFeedback); +#else + (void) aEnableLEDFeedback; + (void) aFeedbackLEDPin; +#endif +} + +#else // defined(IR_SEND_PIN) +IRsend::IRsend(uint_fast8_t aSendPin) { // @suppress("Class members should be properly initialized") + sendPin = aSendPin; +# if !defined(NO_LED_SEND_FEEDBACK_CODE) + setLEDFeedback(0, DO_NOT_ENABLE_LED_FEEDBACK); +# endif +} + +/** + * Initializes the send pin and enable LED feedback with board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions + * @param aSendPin The Arduino pin number, where a IR sender diode is connected. + */ +void IRsend::begin(uint_fast8_t aSendPin) { + sendPin = aSendPin; +# if !defined(NO_LED_SEND_FEEDBACK_CODE) + setLEDFeedback(USE_DEFAULT_FEEDBACK_LED_PIN, LED_FEEDBACK_ENABLED_FOR_SEND); +# endif +} + +void IRsend::setSendPin(uint_fast8_t aSendPin) { + sendPin = aSendPin; +} + +/** + * Initializes the send and feedback pin + * @param aSendPin The Arduino pin number, where a IR sender diode is connected. + * @param aEnableLEDFeedback If true the feedback LED is activated while receiving or sending a PWM signal /a mark + * @param aFeedbackLEDPin If 0 / USE_DEFAULT_FEEDBACK_LED_PIN, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions + */ +void IRsend::begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) { +#if defined(IR_SEND_PIN) + (void) aSendPin; // for backwards compatibility +#else + sendPin = aSendPin; +#endif + +#if !defined(NO_LED_SEND_FEEDBACK_CODE) + uint_fast8_t tEnableLEDFeedback = DO_NOT_ENABLE_LED_FEEDBACK; + if (aEnableLEDFeedback) { + tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_SEND; + } + setLEDFeedback(aFeedbackLEDPin, tEnableLEDFeedback); +#else + (void) aEnableLEDFeedback; + (void) aFeedbackLEDPin; +#endif +} +#endif // defined(IR_SEND_PIN) + +/** + * Interprets and sends a IRData structure. + * @param aIRSendData The values of protocol, address, command and repeat flag are taken for sending. + * @param aNumberOfRepeats Number of repeats to send after the initial data if data is no repeat. + * @return 1 if data sent, 0 if no data sent (i.e. for BANG_OLUFSEN, which is currently not supported here) + */ +/** + * Interprets and sends a IRData structure. + * @param aIRSendData The values of protocol, address, command and repeat flag are taken for sending. + * @param aNumberOfRepeats Number of repeats to send after the initial data if data is no repeat. + * @return 1 if data sent, 0 if no data sent (i.e. for BANG_OLUFSEN, which is currently not supported here) + */ +size_t IRsend::write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats) { + + auto tProtocol = aIRSendData->protocol; + auto tAddress = aIRSendData->address; + auto tCommand = aIRSendData->command; + bool tIsRepeat = (aIRSendData->flags & IRDATA_FLAGS_IS_REPEAT); + if (tIsRepeat) { + aNumberOfRepeats = -1; // if aNumberOfRepeats < 0 then only a special repeat frame will be sent + } +// switch (tProtocol) { // 26 bytes bigger than if, else if, else +// case NEC: +// sendNEC(tAddress, tCommand, aNumberOfRepeats, tSendRepeat); +// break; +// case SAMSUNG: +// sendSamsung(tAddress, tCommand, aNumberOfRepeats); +// break; +// case SONY: +// sendSony(tAddress, tCommand, aNumberOfRepeats, aIRSendData->numberOfBits); +// break; +// case PANASONIC: +// sendPanasonic(tAddress, tCommand, aNumberOfRepeats); +// break; +// case DENON: +// sendDenon(tAddress, tCommand, aNumberOfRepeats); +// break; +// case SHARP: +// sendSharp(tAddress, tCommand, aNumberOfRepeats); +// break; +// case JVC: +// sendJVC((uint8_t) tAddress, (uint8_t) tCommand, aNumberOfRepeats); // casts are required to specify the right function +// break; +// case RC5: +// sendRC5(tAddress, tCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats +// break; +// case RC6: +// // No toggle for repeats// sendRC6(tAddress, tCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats +// break; +// default: +// break; +// } + + /* + * Order of protocols is in guessed relevance :-) + */ + if (tProtocol == NEC) { + sendNEC(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == SAMSUNG) { + sendSamsung(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == SAMSUNG48) { + sendSamsung48(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == SAMSUNGLG) { + sendSamsungLG(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == SONY) { + sendSony(tAddress, tCommand, aNumberOfRepeats, aIRSendData->numberOfBits); + + } else if (tProtocol == PANASONIC) { + sendPanasonic(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == DENON) { + sendDenon(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == SHARP) { + sendSharp(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == LG) { + sendLG(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == JVC) { + sendJVC((uint8_t) tAddress, (uint8_t) tCommand, aNumberOfRepeats); // casts are required to specify the right function + + } else if (tProtocol == RC5) { + sendRC5(tAddress, tCommand, aNumberOfRepeats, !tIsRepeat); // No toggle for repeats + + } else if (tProtocol == RC6) { + sendRC6(tAddress, tCommand, aNumberOfRepeats, !tIsRepeat); // No toggle for repeats + + } else if (tProtocol == KASEIKYO_JVC) { + sendKaseikyo_JVC(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == KASEIKYO_DENON) { + sendKaseikyo_Denon(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == KASEIKYO_SHARP) { + sendKaseikyo_Sharp(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == KASEIKYO_MITSUBISHI) { + sendKaseikyo_Mitsubishi(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == NEC2) { + sendNEC2(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == ONKYO) { + sendOnkyo(tAddress, tCommand, aNumberOfRepeats); + + } else if (tProtocol == APPLE) { + sendApple(tAddress, tCommand, aNumberOfRepeats); + +#if !defined(EXCLUDE_EXOTIC_PROTOCOLS) + } else if (tProtocol == BOSEWAVE) { + sendBoseWave(tCommand, aNumberOfRepeats); + + } else if (tProtocol == MAGIQUEST) { + // we have a 32 bit ID/address + sendMagiQuest(aIRSendData->decodedRawData, tCommand); + + } else if (tProtocol == FAST) { + // We have only 8 bit command + sendFAST(tCommand, aNumberOfRepeats); + + } else if (tProtocol == LEGO_PF) { + sendLegoPowerFunctions(tAddress, tCommand, tCommand >> 4, tIsRepeat); // send 5 autorepeats +#endif + + } else { + return 0; // Not supported by write. E.g for BANG_OLUFSEN + } + return 1; +} + +/** + * Simple version of write without support for MAGIQUEST and numberOfBits for SONY protocol + * @param aNumberOfRepeats If aNumberOfRepeats < 0 then only a special repeat frame without leading and trailing space + * will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction(). + */ +size_t IRsend::write(decode_type_t aProtocol, uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) { + +// switch (aProtocol) { // 26 bytes bigger than if, else if, else +// case NEC: +// sendNEC(aAddress, aCommand, aNumberOfRepeats, tSendRepeat); +// break; +// case SAMSUNG: +// sendSamsung(aAddress, aCommand, aNumberOfRepeats); +// break; +// case SONY: +// sendSony(aAddress, aCommand, aNumberOfRepeats, aIRSendData->numberOfBits); +// break; +// case PANASONIC: +// sendPanasonic(aAddress, aCommand, aNumberOfRepeats); +// break; +// case DENON: +// sendDenon(aAddress, aCommand, aNumberOfRepeats); +// break; +// case SHARP: +// sendSharp(aAddress, aCommand, aNumberOfRepeats); +// break; +// case JVC: +// sendJVC((uint8_t) aAddress, (uint8_t) aCommand, aNumberOfRepeats); // casts are required to specify the right function +// break; +// case RC5: +// sendRC5(aAddress, aCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats +// break; +// case RC6: +// // No toggle for repeats// sendRC6(aAddress, aCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats +// break; +// default: +// break; +// } + + /* + * Order of protocols is in guessed relevance :-) + */ + if (aProtocol == NEC) { + sendNEC(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == SAMSUNG) { + sendSamsung(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == SAMSUNG48) { + sendSamsung48(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == SAMSUNGLG) { + sendSamsungLG(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == SONY) { + sendSony(aAddress, aCommand, aNumberOfRepeats, SIRCS_12_PROTOCOL); + + } else if (aProtocol == PANASONIC) { + sendPanasonic(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == DENON) { + sendDenon(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == SHARP) { + sendSharp(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == LG) { + sendLG(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == JVC) { + sendJVC((uint8_t) aAddress, (uint8_t) aCommand, aNumberOfRepeats); // casts are required to specify the right function + + } else if (aProtocol == RC5) { + sendRC5(aAddress, aCommand, aNumberOfRepeats, (aNumberOfRepeats > 0)); // No toggle for repeats + + } else if (aProtocol == RC6) { + sendRC6(aAddress, aCommand, aNumberOfRepeats, (aNumberOfRepeats > 0)); // No toggle for repeats + + } else if (aProtocol == KASEIKYO_JVC) { + sendKaseikyo_JVC(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == KASEIKYO_DENON) { + sendKaseikyo_Denon(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == KASEIKYO_SHARP) { + sendKaseikyo_Sharp(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == KASEIKYO_MITSUBISHI) { + sendKaseikyo_Mitsubishi(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == NEC2) { + sendNEC2(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == ONKYO) { + sendOnkyo(aAddress, aCommand, aNumberOfRepeats); + + } else if (aProtocol == APPLE) { + sendApple(aAddress, aCommand, aNumberOfRepeats); + +#if !defined(EXCLUDE_EXOTIC_PROTOCOLS) + } else if (aProtocol == BOSEWAVE) { + sendBoseWave(aCommand, aNumberOfRepeats); + + } else if (aProtocol == FAST) { + // We have only 8 bit command + sendFAST(aCommand, aNumberOfRepeats); + + } else if (aProtocol == LEGO_PF) { + sendLegoPowerFunctions(aAddress, aCommand, aCommand >> 4, (aNumberOfRepeats < 0)); // send 5 autorepeats, except for dedicated repeats +#endif + + } else { + return 0; // Not supported by write. E.g for BANG_OLUFSEN + } + return 1; +} + +/** + * Sends a 16 byte microsecond timing array. + * Raw data starts with a Mark. No leading space as in received timing data! + */ +void IRsend::sendRaw(const uint16_t aBufferWithMicroseconds[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz) { +// Set IR carrier frequency + enableIROut(aIRFrequencyKilohertz); + + /* + * Raw data starts with a mark. + */ + for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) { + if (i & 1) { + // Odd + space(aBufferWithMicroseconds[i]); + } else { + mark(aBufferWithMicroseconds[i]); + } + } +} + +/** + * Sends an 8 byte tick timing array to save program memory. + * Raw data starts with a Mark. No leading space as in received timing data! + */ +void IRsend::sendRaw(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz) { +// Set IR carrier frequency + enableIROut(aIRFrequencyKilohertz); + + for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) { + if (i & 1) { + // Odd + space(aBufferWithTicks[i] * MICROS_PER_TICK); + } else { + mark(aBufferWithTicks[i] * MICROS_PER_TICK); + } + } + IRLedOff(); // Always end with the LED off +} + +/** + * Function using an 16 byte microsecond timing array in FLASH for every purpose. + * Raw data starts with a Mark. No leading space as in received timing data! + */ +void IRsend::sendRaw_P(const uint16_t aBufferWithMicroseconds[], uint_fast16_t aLengthOfBuffer, + uint_fast8_t aIRFrequencyKilohertz) { +#if !defined(__AVR__) + sendRaw(aBufferWithMicroseconds, aLengthOfBuffer, aIRFrequencyKilohertz); // Let the function work for non AVR platforms +#else +// Set IR carrier frequency + enableIROut(aIRFrequencyKilohertz); + /* + * Raw data starts with a mark + */ + for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) { + auto duration = pgm_read_word(&aBufferWithMicroseconds[i]); + if (i & 1) { + // Odd + space(duration); +# if defined(LOCAL_DEBUG) + Serial.print(F("S=")); +# endif + } else { + mark(duration); +# if defined(LOCAL_DEBUG) + Serial.print(F("M=")); +# endif + } +# if defined(LOCAL_DEBUG) + Serial.println(duration); +# endif + } +#endif +} + +/** + * New function using an 8 byte tick (50 us) timing array in FLASH to save program memory + * Raw data starts with a Mark. No leading space as in received timing data! + */ +void IRsend::sendRaw_P(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz) { +#if !defined(__AVR__) + sendRaw(aBufferWithTicks, aLengthOfBuffer, aIRFrequencyKilohertz); // Let the function work for non AVR platforms +#else +// Set IR carrier frequency + enableIROut(aIRFrequencyKilohertz); + + uint_fast16_t duration; + for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) { + duration = pgm_read_byte(&aBufferWithTicks[i]) * (uint_fast16_t) MICROS_PER_TICK; + if (i & 1) { + // Odd + space(duration); +# if defined(LOCAL_DEBUG) + Serial.print(F("S=")); +# endif + } else { + mark(duration); +# if defined(LOCAL_DEBUG) + Serial.print(F("M=")); +# endif + } + } + IRLedOff(); // Always end with the LED off +# if defined(LOCAL_DEBUG) + Serial.println(duration); +# endif +#endif +} + +/** + * Sends PulseDistance data from array + * For LSB First the LSB of array[0] is sent first then all bits until MSB of array[0]. Next is LSB of array[1] and so on. + * The output always ends with a space + * Stop bit is always sent + * @param aFlags Evaluated flags are PROTOCOL_IS_MSB_FIRST and SUPPRESS_STOP_BIT. Stop bit is otherwise sent for all pulse distance protocols. + */ +void IRsend::sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, DistanceWidthTimingInfoStruct *aDistanceWidthTimingInfo, + IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, + int_fast8_t aNumberOfRepeats) { + sendPulseDistanceWidthFromArray(aFrequencyKHz, aDistanceWidthTimingInfo->HeaderMarkMicros, + aDistanceWidthTimingInfo->HeaderSpaceMicros, aDistanceWidthTimingInfo->OneMarkMicros, + aDistanceWidthTimingInfo->OneSpaceMicros, aDistanceWidthTimingInfo->ZeroMarkMicros, + aDistanceWidthTimingInfo->ZeroSpaceMicros, aDecodedRawDataArray, aNumberOfBits, aFlags, aRepeatPeriodMillis, + aNumberOfRepeats); +} +void IRsend::sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros, + uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, + IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, + int_fast8_t aNumberOfRepeats) { + + // Set IR carrier frequency + enableIROut(aFrequencyKHz); + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + uint_fast8_t tNumberOf32Or64BitChunks = ((aNumberOfBits - 1) / BITS_IN_RAW_DATA_TYPE) + 1; + +#if defined(LOCAL_DEBUG) + // fist data + Serial.print(F("Data[0]=0x")); + Serial.print(aDecodedRawDataArray[0], HEX); + if (tNumberOf32Or64BitChunks > 1) { + Serial.print(F(" Data[1]=0x")); + Serial.print(aDecodedRawDataArray[1], HEX); + } + Serial.print(F(" #=")); + Serial.println(aNumberOfBits); + Serial.flush(); +#endif + + while (tNumberOfCommands > 0) { + unsigned long tStartOfFrameMillis = millis(); + + // Header + mark(aHeaderMarkMicros); + space(aHeaderSpaceMicros); + + for (uint_fast8_t i = 0; i < tNumberOf32Or64BitChunks; ++i) { + uint8_t tNumberOfBitsForOneSend; + + // Manage stop bit + uint8_t tFlags; + if (i == (tNumberOf32Or64BitChunks - 1)) { + // End of data + tNumberOfBitsForOneSend = aNumberOfBits; + tFlags = aFlags; + } else { + // intermediate data + tNumberOfBitsForOneSend = BITS_IN_RAW_DATA_TYPE; + tFlags = aFlags | SUPPRESS_STOP_BIT; // No stop bit for leading data + } + + sendPulseDistanceWidthData(aOneMarkMicros, aOneSpaceMicros, aZeroMarkMicros, aZeroSpaceMicros, aDecodedRawDataArray[i], + tNumberOfBitsForOneSend, tFlags); + aNumberOfBits -= BITS_IN_RAW_DATA_TYPE; + } + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + /* + * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration. + */ + auto tFrameDurationMillis = millis() - tStartOfFrameMillis; + if (aRepeatPeriodMillis > tFrameDurationMillis) { + delay(aRepeatPeriodMillis - tFrameDurationMillis); + } + } + } +} + +void IRsend::sendPulseDistanceWidthFromPGMArray(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros, + uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, + IRRawDataType const *aDecodedRawDataPGMArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, + int_fast8_t aNumberOfRepeats) { + + // Set IR carrier frequency + enableIROut(aFrequencyKHz); + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + uint_fast8_t tNumberOf32Or64BitChunks = ((aNumberOfBits - 1) / BITS_IN_RAW_DATA_TYPE) + 1; + +#if defined(LOCAL_DEBUG) + // fist data + Serial.print(F("Data[0]=0x")); + Serial.print(aDecodedRawDataPGMArray[0], HEX); + if (tNumberOf32Or64BitChunks > 1) { + Serial.print(F(" Data[1]=0x")); + Serial.print(aDecodedRawDataPGMArray[1], HEX); + } + Serial.print(F(" #=")); + Serial.println(aNumberOfBits); + Serial.flush(); +#endif + + while (tNumberOfCommands > 0) { + unsigned long tStartOfFrameMillis = millis(); + + // Header + mark(aHeaderMarkMicros); + space(aHeaderSpaceMicros); + + for (uint_fast8_t i = 0; i < tNumberOf32Or64BitChunks; ++i) { + uint8_t tNumberOfBitsForOneSend; + + // Manage stop bit + uint8_t tFlags; + if (i == (tNumberOf32Or64BitChunks - 1)) { + // End of data + tNumberOfBitsForOneSend = aNumberOfBits; + tFlags = aFlags; + } else { + // intermediate data + tNumberOfBitsForOneSend = BITS_IN_RAW_DATA_TYPE; + tFlags = aFlags | SUPPRESS_STOP_BIT; // No stop bit for leading data + } + + IRRawDataType tDecodedRawData; +#if (__INT_WIDTH__ < 32) + tDecodedRawData = pgm_read_dword(&aDecodedRawDataPGMArray[i]); // pgm_read_dword reads 32 bit on AVR +#else + tDecodedRawData = aDecodedRawDataPGMArray[i]; // assume non Harvard architecture here +#endif + sendPulseDistanceWidthData(aOneMarkMicros, aOneSpaceMicros, aZeroMarkMicros, aZeroSpaceMicros, tDecodedRawData, + tNumberOfBitsForOneSend, tFlags); + aNumberOfBits -= BITS_IN_RAW_DATA_TYPE; + } + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + /* + * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration. + */ + auto tFrameDurationMillis = millis() - tStartOfFrameMillis; + if (aRepeatPeriodMillis > tFrameDurationMillis) { + delay(aRepeatPeriodMillis - tFrameDurationMillis); + } + } + } +} + +void IRsend::sendPulseDistanceWidthFromArray_P(uint_fast8_t aFrequencyKHz, + DistanceWidthTimingInfoStruct const *aDistanceWidthTimingInfoPGM, IRRawDataType *aDecodedRawDataArray, + uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, int_fast8_t aNumberOfRepeats) { + + DistanceWidthTimingInfoStruct tTemporaryDistanceWidthTimingInfo; + memcpy_P(&tTemporaryDistanceWidthTimingInfo, aDistanceWidthTimingInfoPGM, sizeof(tTemporaryDistanceWidthTimingInfo)); + sendPulseDistanceWidthFromArray(aFrequencyKHz, &tTemporaryDistanceWidthTimingInfo, aDecodedRawDataArray, aNumberOfBits, aFlags, + aRepeatPeriodMillis, aNumberOfRepeats); +} + +/** + * Sends PulseDistance data from array using PulseDistanceWidthProtocolConstants + * For LSB First the LSB of array[0] is sent first then all bits until MSB of array[0]. Next is LSB of array[1] and so on. + * The output always ends with a space + * Stop bit is always sent + * @param aNumberOfBits Number of bits from aDecodedRawDataArray to be actually sent. + * @param aNumberOfRepeats If < 0 and a aProtocolConstants->SpecialSendRepeatFunction() is specified + * then it is called without leading and trailing space. + */ +void IRsend::sendPulseDistanceWidthFromArray(PulseDistanceWidthProtocolConstants *aProtocolConstants, + IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, int_fast8_t aNumberOfRepeats) { + +// Calling sendPulseDistanceWidthFromArray() costs 68 bytes program memory compared to the implementation below +// sendPulseDistanceWidthFromArray(aProtocolConstants->FrequencyKHz, aProtocolConstants->DistanceWidthTimingInfo.HeaderMarkMicros, +// aProtocolConstants->DistanceWidthTimingInfo.HeaderSpaceMicros, +// aProtocolConstants->DistanceWidthTimingInfo.OneMarkMicros, aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros, +// aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros, aProtocolConstants->DistanceWidthTimingInfo.ZeroSpaceMicros, +// aDecodedRawDataArray, aNumberOfBits, aProtocolConstants->Flags, aProtocolConstants->RepeatPeriodMillis, +// aNumberOfRepeats); + // Set IR carrier frequency + enableIROut(aProtocolConstants->FrequencyKHz); + + uint_fast8_t tNumberOf32Or64BitChunks = ((aNumberOfBits - 1) / BITS_IN_RAW_DATA_TYPE) + 1; + +#if defined(LOCAL_DEBUG) + // fist data + Serial.print(F("Data[0]=0x")); + Serial.print(aDecodedRawDataArray[0], HEX); + if (tNumberOf32Or64BitChunks > 1) { + Serial.print(F(" Data[1]=0x")); + Serial.print(aDecodedRawDataArray[1], HEX); + } + Serial.print(F(" #=")); + Serial.println(aNumberOfBits); + Serial.flush(); +#endif + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + auto tStartOfFrameMillis = millis(); + auto tNumberOfBits = aNumberOfBits; // refresh value for repeats + + // Header + mark(aProtocolConstants->DistanceWidthTimingInfo.HeaderMarkMicros); + space(aProtocolConstants->DistanceWidthTimingInfo.HeaderSpaceMicros); + uint8_t tOriginalFlags = aProtocolConstants->Flags; + + for (uint_fast8_t i = 0; i < tNumberOf32Or64BitChunks; ++i) { + uint8_t tNumberOfBitsForOneSend; + + uint8_t tFlags; + if (i == (tNumberOf32Or64BitChunks - 1)) { + // End of data + tNumberOfBitsForOneSend = tNumberOfBits; + tFlags = tOriginalFlags; + } else { + // intermediate data + tNumberOfBitsForOneSend = BITS_IN_RAW_DATA_TYPE; + tFlags = tOriginalFlags | SUPPRESS_STOP_BIT; // No stop bit for leading data + } + + sendPulseDistanceWidthData(aProtocolConstants->DistanceWidthTimingInfo.OneMarkMicros, + aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros, + aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros, + aProtocolConstants->DistanceWidthTimingInfo.ZeroSpaceMicros, aDecodedRawDataArray[i], tNumberOfBitsForOneSend, + tFlags); + tNumberOfBits -= BITS_IN_RAW_DATA_TYPE; + } + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + /* + * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration. + */ + auto tFrameDurationMillis = millis() - tStartOfFrameMillis; + if (aProtocolConstants->RepeatPeriodMillis > tFrameDurationMillis) { + delay(aProtocolConstants->RepeatPeriodMillis - tFrameDurationMillis); + } + } + } +} + +void IRsend::sendPulseDistanceWidthFromArray_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, + IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, int_fast8_t aNumberOfRepeats) { + + PulseDistanceWidthProtocolConstants tTemporaryPulseDistanceWidthProtocolConstants; + memcpy_P(&tTemporaryPulseDistanceWidthProtocolConstants, aProtocolConstantsPGM, + sizeof(tTemporaryPulseDistanceWidthProtocolConstants)); + sendPulseDistanceWidthFromArray(&tTemporaryPulseDistanceWidthProtocolConstants, aDecodedRawDataArray, aNumberOfBits, + aNumberOfRepeats); +} +void IRsend::sendPulseDistanceWidthFromPGMArray_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, + IRRawDataType const *aDecodedRawDataPGMArray, uint16_t aNumberOfBits, int_fast8_t aNumberOfRepeats) { + + PulseDistanceWidthProtocolConstants tTemporaryPulseDistanceWidthProtocolConstants; + memcpy_P(&tTemporaryPulseDistanceWidthProtocolConstants, aProtocolConstantsPGM, + sizeof(tTemporaryPulseDistanceWidthProtocolConstants)); + sendPulseDistanceWidthFromPGMArray(&tTemporaryPulseDistanceWidthProtocolConstants, aDecodedRawDataPGMArray, aNumberOfBits, + aNumberOfRepeats); +} + +void IRsend::sendPulseDistanceWidthFromPGMArray(PulseDistanceWidthProtocolConstants *aProtocolConstants, + IRRawDataType const *aDecodedRawDataPGMArray, uint16_t aNumberOfBits, int_fast8_t aNumberOfRepeats) { + +// Calling sendPulseDistanceWidthFromArray() costs 68 bytes program memory compared to the implementation below +// sendPulseDistanceWidthFromArray(aProtocolConstants->FrequencyKHz, aProtocolConstants->DistanceWidthTimingInfo.HeaderMarkMicros, +// aProtocolConstants->DistanceWidthTimingInfo.HeaderSpaceMicros, +// aProtocolConstants->DistanceWidthTimingInfo.OneMarkMicros, aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros, +// aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros, aProtocolConstants->DistanceWidthTimingInfo.ZeroSpaceMicros, +// aDecodedRawDataArray, aNumberOfBits, aProtocolConstants->Flags, aProtocolConstants->RepeatPeriodMillis, +// aNumberOfRepeats); + // Set IR carrier frequency + enableIROut(aProtocolConstants->FrequencyKHz); + + uint_fast8_t tNumberOf32Or64BitChunks = ((aNumberOfBits - 1) / BITS_IN_RAW_DATA_TYPE) + 1; + +#if defined(LOCAL_DEBUG) + // fist data + Serial.print(F("Data[0]=0x")); + Serial.print(aDecodedRawDataPGMArray[0], HEX); + if (tNumberOf32Or64BitChunks > 1) { + Serial.print(F(" Data[1]=0x")); + Serial.print(aDecodedRawDataPGMArray[1], HEX); + } + Serial.print(F(" #=")); + Serial.println(aNumberOfBits); + Serial.flush(); +#endif + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + auto tStartOfFrameMillis = millis(); + auto tNumberOfBits = aNumberOfBits; // refresh value for repeats + + // Header + mark(aProtocolConstants->DistanceWidthTimingInfo.HeaderMarkMicros); + space(aProtocolConstants->DistanceWidthTimingInfo.HeaderSpaceMicros); + uint8_t tOriginalFlags = aProtocolConstants->Flags; + + for (uint_fast8_t i = 0; i < tNumberOf32Or64BitChunks; ++i) { + uint8_t tNumberOfBitsForOneSend; + + uint8_t tFlags; + if (i == (tNumberOf32Or64BitChunks - 1)) { + // End of data + tNumberOfBitsForOneSend = tNumberOfBits; + tFlags = tOriginalFlags; + } else { + // intermediate data + tNumberOfBitsForOneSend = BITS_IN_RAW_DATA_TYPE; + tFlags = tOriginalFlags | SUPPRESS_STOP_BIT; // No stop bit for leading data + } + + IRRawDataType tDecodedRawData; +#if (__INT_WIDTH__ < 32) + tDecodedRawData = pgm_read_dword(&aDecodedRawDataPGMArray[i]); // pgm_read_dword reads 32 bit on AVR +#else + tDecodedRawData = aDecodedRawDataPGMArray[i]; // assume non Harvard architecture here +#endif + sendPulseDistanceWidthData(aProtocolConstants->DistanceWidthTimingInfo.OneMarkMicros, + aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros, + aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros, + aProtocolConstants->DistanceWidthTimingInfo.ZeroSpaceMicros, tDecodedRawData, tNumberOfBitsForOneSend, tFlags); + tNumberOfBits -= BITS_IN_RAW_DATA_TYPE; + } + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + /* + * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration. + */ + auto tFrameDurationMillis = millis() - tStartOfFrameMillis; + if (aProtocolConstants->RepeatPeriodMillis > tFrameDurationMillis) { + delay(aProtocolConstants->RepeatPeriodMillis - tFrameDurationMillis); + } + } + } +} + +/** + * Sends PulseDistance frames and repeats + * @param aProtocolConstants The constants to use for sending this protocol. + * @param aData uint32 or uint64 holding the bits to be sent. + * @param aNumberOfBits Number of bits from aData to be actually sent. + * @param aNumberOfRepeats If < 0 and a aProtocolConstants->SpecialSendRepeatFunction() is specified + * then it is called without leading and trailing space. + */ +void IRsend::sendPulseDistanceWidth(PulseDistanceWidthProtocolConstants *aProtocolConstants, IRRawDataType aData, + uint_fast8_t aNumberOfBits, int_fast8_t aNumberOfRepeats) { + +#if defined(LOCAL_DEBUG) + Serial.print(F("Data=0x")); + Serial.print(aData, HEX); + Serial.print(F(" #=")); + Serial.println(aNumberOfBits); + Serial.flush(); +#endif + + if (aNumberOfRepeats < 0) { + if (aProtocolConstants->SpecialSendRepeatFunction != nullptr) { + /* + * Send only a special repeat and return + */ + aProtocolConstants->SpecialSendRepeatFunction(); + return; + } else { + // Send only one plain frame (as repeat) + aNumberOfRepeats = 0; + } + } + + // Set IR carrier frequency + enableIROut(aProtocolConstants->FrequencyKHz); + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + unsigned long tStartOfFrameMillis = millis(); + + if (tNumberOfCommands < ((uint_fast8_t) aNumberOfRepeats + 1) && aProtocolConstants->SpecialSendRepeatFunction != nullptr) { + // send special repeat, if specified and we are not in the first loop + aProtocolConstants->SpecialSendRepeatFunction(); + } else { + /* + * Send Header and regular frame + */ + mark(aProtocolConstants->DistanceWidthTimingInfo.HeaderMarkMicros); + space(aProtocolConstants->DistanceWidthTimingInfo.HeaderSpaceMicros); + sendPulseDistanceWidthData(aProtocolConstants, aData, aNumberOfBits); + } + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + auto tCurrentFrameDurationMillis = millis() - tStartOfFrameMillis; + /* + * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration. + */ + if (aProtocolConstants->RepeatPeriodMillis > tCurrentFrameDurationMillis) { + delay(aProtocolConstants->RepeatPeriodMillis - tCurrentFrameDurationMillis); + } + } + } +} +void IRsend::sendPulseDistanceWidth_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, IRRawDataType aData, + uint_fast8_t aNumberOfBits, int_fast8_t aNumberOfRepeats) { + PulseDistanceWidthProtocolConstants tTemporaryPulseDistanceWidthProtocolConstants; + memcpy_P(&tTemporaryPulseDistanceWidthProtocolConstants, aProtocolConstantsPGM, + sizeof(tTemporaryPulseDistanceWidthProtocolConstants)); + sendPulseDistanceWidth(&tTemporaryPulseDistanceWidthProtocolConstants, aData, aNumberOfBits, aNumberOfRepeats); +} + +/** + * Sends PulseDistance frames and repeats. + * @param aFrequencyKHz, aHeaderMarkMicros, aHeaderSpaceMicros, aOneMarkMicros, aOneSpaceMicros, aZeroMarkMicros, aZeroSpaceMicros, aFlags, aRepeatPeriodMillis Values to use for sending this protocol, also contained in the PulseDistanceWidthProtocolConstants of this protocol. + * @param aData uint32 or uint64 holding the bits to be sent. + * @param aNumberOfBits Number of bits from aData to be actually sent. + * @param aFlags Evaluated flags are PROTOCOL_IS_MSB_FIRST and SUPPRESS_STOP_BIT. Stop bit is otherwise sent for all pulse distance protocols. + * @param aNumberOfRepeats If < 0 and a aProtocolConstants->SpecialSendRepeatFunction() is specified + * then it is called without leading and trailing space. + * @param aSpecialSendRepeatFunction If nullptr, the first frame is repeated completely, otherwise this function is used for sending the repeat frame. + */ +void IRsend::sendPulseDistanceWidth(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros, + uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, IRRawDataType aData, + uint_fast8_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, int_fast8_t aNumberOfRepeats, + void (*aSpecialSendRepeatFunction)()) { + + if (aNumberOfRepeats < 0) { + if (aSpecialSendRepeatFunction != nullptr) { + aSpecialSendRepeatFunction(); + return; + } else { + aNumberOfRepeats = 0; // send a plain frame as repeat + } + } + + // Set IR carrier frequency + enableIROut(aFrequencyKHz); + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + unsigned long tStartOfFrameMillis = millis(); + + if (tNumberOfCommands < ((uint_fast8_t) aNumberOfRepeats + 1) && aSpecialSendRepeatFunction != nullptr) { + // send special repeat + aSpecialSendRepeatFunction(); + } else { + // Header and regular frame + mark(aHeaderMarkMicros); + space(aHeaderSpaceMicros); + sendPulseDistanceWidthData(aOneMarkMicros, aOneSpaceMicros, aZeroMarkMicros, aZeroSpaceMicros, aData, aNumberOfBits, + aFlags); + } + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + /* + * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration. + */ + auto tFrameDurationMillis = millis() - tStartOfFrameMillis; + if (aRepeatPeriodMillis > tFrameDurationMillis) { + delay(aRepeatPeriodMillis - tFrameDurationMillis); + } + } + } +} + +/** + * Sends PulseDistance from data contained in parameter using ProtocolConstants structure for timing etc. + * The output always ends with a space + * Each additional call costs 16 bytes program memory + * @param aProtocolConstants The constants to use for sending this protocol. + * @param aData uint32 or uint64 holding the bits to be sent. + * @param aNumberOfBits Number of bits from aData to be actually sent. + */ +void IRsend::sendPulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, IRRawDataType aData, + uint_fast8_t aNumberOfBits) { + + sendPulseDistanceWidthData(aProtocolConstants->DistanceWidthTimingInfo.OneMarkMicros, + aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros, aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros, + aProtocolConstants->DistanceWidthTimingInfo.ZeroSpaceMicros, aData, aNumberOfBits, aProtocolConstants->Flags); +} + +void IRsend::sendPulseDistanceWidthData_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, IRRawDataType aData, + uint_fast8_t aNumberOfBits) { + + PulseDistanceWidthProtocolConstants tTemporaryPulseDistanceWidthProtocolConstants; + memcpy_P(&tTemporaryPulseDistanceWidthProtocolConstants, aProtocolConstantsPGM, + sizeof(tTemporaryPulseDistanceWidthProtocolConstants)); + sendPulseDistanceWidthData(&tTemporaryPulseDistanceWidthProtocolConstants, aData, aNumberOfBits); +} + +/** + * Sends PulseDistance data with timing parameters and flag parameters. + * The output always ends with a space + * @param aOneMarkMicros Timing for sending this protocol. + * @param aData uint32 or uint64 holding the bits to be sent. + * @param aNumberOfBits Number of bits from aData to be actually sent. + * @param aFlags Evaluated flags are PROTOCOL_IS_MSB_FIRST and SUPPRESS_STOP_BIT. Stop bit is otherwise sent for all pulse distance protocols. + */ +void IRsend::sendPulseDistanceWidthData(uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, + uint16_t aZeroSpaceMicros, IRRawDataType aData, uint_fast8_t aNumberOfBits, uint8_t aFlags) { + +#if defined(LOCAL_DEBUG) + Serial.print(aData, HEX); + Serial.print('|'); + Serial.println(aNumberOfBits); + Serial.flush(); +#endif + + // For MSBFirst, send data from MSB to LSB until mask bit is shifted out + IRRawDataType tMask = 1ULL << (aNumberOfBits - 1); + for (uint_fast8_t i = aNumberOfBits; i > 0; i--) { + if (((aFlags & PROTOCOL_IS_MSB_FIRST) && (aData & tMask)) || (!(aFlags & PROTOCOL_IS_MSB_FIRST) && (aData & 1))) { +#if defined(LOCAL_TRACE) + Serial.print('1'); +#endif + mark(aOneMarkMicros); + space(aOneSpaceMicros); + } else { +#if defined(LOCAL_TRACE) + Serial.print('0'); +#endif + mark(aZeroMarkMicros); + space(aZeroSpaceMicros); + } + if (aFlags & PROTOCOL_IS_MSB_FIRST) { + tMask >>= 1; + } else { + aData >>= 1; + } + } + /* + * Stop bit is sent for all pulse distance protocols i.e. aOneSpaceMicros != aZeroSpaceMicros. + * Therefore it is not sent for Sony :-) + * For sending from an array, no intermediate stop bit must be sent for all but last data chunk. + */ + if ((!(aFlags & SUPPRESS_STOP_BIT)) && (abs(aOneSpaceMicros - aZeroSpaceMicros) > (aOneSpaceMicros / 4))) { + // Send stop bit here +#if defined(LOCAL_TRACE) + Serial.print('S'); +#endif + mark(aOneMarkMicros); // Use aOneMarkMicros for stop bits. This seems to be correct for all protocols :-) + } +#if defined(LOCAL_TRACE) + Serial.println(); +#endif +} + +/** + * Sends Biphase data MSB first + * Always send start bit, do not send the trailing space of the start bit + * 0 -> mark+space + * 1 -> space+mark + * The output always ends with a space + * can only send 31 bit data, since we put the start bit as 32th bit on front + * @param aData uint32 or uint64 holding the bits to be sent. + * @param aNumberOfBits Number of bits from aData to be actually sent. + */ +void IRsend::sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits) { + + IR_TRACE_PRINT(F("0x")); + IR_TRACE_PRINT(aData, HEX); + +#if defined(LOCAL_TRACE) + Serial.print('S'); +#endif + +// Data - Biphase code MSB first +// prepare for start with sending the start bit, which is 1 + uint32_t tMask = 1UL << aNumberOfBits; // mask is now set for the virtual start bit + uint_fast8_t tLastBitValue = 1; // Start bit is a 1 + bool tNextBitIsOne = 1; // Start bit is a 1 + for (uint_fast8_t i = aNumberOfBits + 1; i > 0; i--) { + bool tCurrentBitIsOne = tNextBitIsOne; + tMask >>= 1; + tNextBitIsOne = ((aData & tMask) != 0) || (i == 1); // true for last bit to avoid extension of mark + if (tCurrentBitIsOne) { +#if defined(LOCAL_TRACE) + Serial.print('1'); +#endif + space(aBiphaseTimeUnit); + if (tNextBitIsOne) { + mark(aBiphaseTimeUnit); + } else { + // if next bit is 0, extend the current mark in order to generate a continuous signal without short breaks + mark(2 * aBiphaseTimeUnit); + } + tLastBitValue = 1; + + } else { +#if defined(LOCAL_TRACE) + Serial.print('0'); +#endif + if (!tLastBitValue) { + mark(aBiphaseTimeUnit); + } + space(aBiphaseTimeUnit); + tLastBitValue = 0; + } + } + IR_TRACE_PRINTLN(); +} + +/** + * Sends an IR mark for the specified number of microseconds. + * The mark output is modulated at the PWM frequency if USE_NO_SEND_PWM is not defined. + * The output is guaranteed to be OFF / inactive after after the call of the function. + * This function may affect the state of feedback LED. + * Period time is 26 us for 38.46 kHz, 27 us for 37.04 kHz, 25 us for 40 kHz. + * On time is 8 us for 30% duty cycle + * + * The mark() function relies on the correct implementation of: + * delayMicroseconds() for pulse time, and micros() for pause time. + * The delayMicroseconds() of pulse time is guarded on AVR CPU's with noInterrupts() / interrupts(). + * At the start of pause time, interrupts are enabled once, the rest of the pause is also guarded on AVR CPU's with noInterrupts() / interrupts(). + * The maximum length of an interrupt during sending should not exceed 26 us - 8 us = 18 us, otherwise timing is disturbed. + * This disturbance is no problem, if the exceedance is small and does not happen too often. + */ +void IRsend::mark(uint16_t aMarkMicros) { + +#if defined(SEND_PWM_BY_TIMER) || defined(USE_NO_SEND_PWM) +# if !defined(NO_LED_SEND_FEEDBACK_CODE) + if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_SEND) { + setFeedbackLED(true); + } +# endif +#endif + +#if defined(SEND_PWM_BY_TIMER) + /* + * Generate hardware PWM signal + */ + enableSendPWMByTimer(); // Enable timer or ledcWrite() generated PWM output + customDelayMicroseconds(aMarkMicros); + IRLedOff(); // disables hardware PWM and manages feedback LED + return; + +#elif defined(USE_NO_SEND_PWM) + /* + * Here we generate no carrier PWM, just simulate an active low receiver signal. + */ +# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN) + // Here we have no hardware supported Open Drain outputs, so we must mimicking it + pinModeFast(sendPin, OUTPUT); // active state for mimicking open drain +# elif defined(USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN) + digitalWriteFast(sendPin, HIGH); // Set output to active high. +# else + digitalWriteFast(sendPin, LOW); // Set output to active low. +# endif + + customDelayMicroseconds(aMarkMicros); + IRLedOff(); +# if !defined(NO_LED_SEND_FEEDBACK_CODE) + if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_SEND) { + setFeedbackLED(false); + } + return; +# endif + +#else // defined(SEND_PWM_BY_TIMER) + /* + * Generate PWM by bit banging + */ + unsigned long tStartMicros = micros(); + unsigned long tNextPeriodEnding = tStartMicros; + unsigned long tMicros; +# if !defined(NO_LED_SEND_FEEDBACK_CODE) + bool FeedbackLedIsActive = false; +# endif + + do { +// digitalToggleFast(_IR_TIMING_TEST_PIN); + /* + * Output the PWM pulse + */ + noInterrupts(); // do not let interrupts extend the short on period +# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) +# if defined(OUTPUT_OPEN_DRAIN) + digitalWriteFast(sendPin, LOW); // set output with pin mode OUTPUT_OPEN_DRAIN to active low +# else + pinModeFast(sendPin, OUTPUT); // active state for mimicking open drain +# endif +# else + // 3.5 us from FeedbackLed on to pin setting. 5.7 us from call of mark() to pin setting incl. setting of feedback pin. + // 4.3 us from do{ to pin setting if sendPin is no constant + // check must be here because of MegaTinyCore and its badArg() check + if (__builtin_constant_p(sendPin)) { + digitalWriteFast(sendPin, HIGH); + } else { + digitalWrite(sendPin, HIGH); + } +# endif + delayMicroseconds (periodOnTimeMicros); // On time is 8 us for 30% duty cycle. This is normally implemented by a blocking wait. + + /* + * Output the PWM pause + */ +# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN) +# if defined(OUTPUT_OPEN_DRAIN) + digitalWriteFast(sendPin, HIGH); // Set output with pin mode OUTPUT_OPEN_DRAIN to inactive high. +# else + pinModeFast(sendPin, INPUT); // to mimic the open drain inactive state +# endif + +# else + if (__builtin_constant_p(sendPin)) { + digitalWriteFast(sendPin, LOW); + } else { + digitalWrite(sendPin, LOW); + } +# endif + /* + * Enable interrupts at start of the longer off period. Required at least to keep micros correct. + * If receive interrupt is still active, it takes 3.4 us from now until receive ISR is active (for 7 us + pop's) + */ + interrupts(); + +# if !defined(NO_LED_SEND_FEEDBACK_CODE) + /* + * Delayed call of setFeedbackLED() to get better startup timing, especially required for consecutive marks + */ + if (!FeedbackLedIsActive) { + FeedbackLedIsActive = true; + if (FeedbackLEDControl.LedFeedbackEnabled & LED_FEEDBACK_ENABLED_FOR_SEND) { + setFeedbackLED(true); + } + } +# endif + /* + * PWM pause timing + * Measured delta between pause duration values are 13 us for a 16 MHz Uno (from 13 to 26), if interrupts are disabled below + * Measured delta between pause duration values are 20 us for a 16 MHz Uno (from 7.8 to 28), if interrupts are not disabled below + * Minimal pause duration is 5.2 us with NO_LED_SEND_FEEDBACK_CODE enabled + * and 8.1 us with NO_LED_SEND_FEEDBACK_CODE disabled. + */ + tNextPeriodEnding += periodTimeMicros; +#if defined(__AVR__) // micros() for STM sometimes give decreasing values if interrupts are disabled. See https://github.com/stm32duino/Arduino_Core_STM32/issues/1680 + noInterrupts(); // disable interrupts (especially the 20 us receive interrupts) only at start of the PWM pause. Otherwise it may extend the pause too much. +#endif + do { +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) + digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles +#endif + /* + * For AVR @16MHz we have only 4 us resolution. + * The duration of the micros() call itself is 3 us. + * It takes 0.9 us from signal going low here. + * The rest of the loop takes 1.2 us with NO_LED_SEND_FEEDBACK_CODE enabled + * and 3 us with NO_LED_SEND_FEEDBACK_CODE disabled. + */ +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) + digitalWriteFast(_IR_TIMING_TEST_PIN, LOW); // 2 clock cycles +#endif + /* + * Exit the forever loop if aMarkMicros has reached + */ + tMicros = micros(); + uint16_t tDeltaMicros = tMicros - tStartMicros; +#if defined(__AVR__) + // reset feedback led in the last pause before end +// tDeltaMicros += (160 / CLOCKS_PER_MICRO); // adding this once increases program size, so do it below ! +# if !defined(NO_LED_SEND_FEEDBACK_CODE) + if (tDeltaMicros >= aMarkMicros - (30 + (112 / CLOCKS_PER_MICRO))) { // 30 to be constant. Using periodTimeMicros increases program size too much. + if (FeedbackLEDControl.LedFeedbackEnabled & LED_FEEDBACK_ENABLED_FOR_SEND) { + setFeedbackLED(false); + } + } +# endif + // Just getting variables and check for end condition takes minimal 3.8 us + if (tDeltaMicros >= aMarkMicros - (112 / CLOCKS_PER_MICRO)) { // To compensate for call duration - 112 is an empirical value +#else + if (tDeltaMicros >= aMarkMicros) { +# if !defined(NO_LED_SEND_FEEDBACK_CODE) + if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_SEND) { + setFeedbackLED(false); + } +# endif +#endif +#if defined(__AVR__) + interrupts(); +#endif + return; + } + } while (tMicros < tNextPeriodEnding); + } while (true); +# endif +} + +/** + * Just switch the IR sending LED off to send an IR space + * A space is "no output", so the PWM output is disabled. + * This function may affect the state of feedback LED. + */ +void IRsend::IRLedOff() { +#if defined(SEND_PWM_BY_TIMER) + disableSendPWMByTimer(); // Disable PWM output +#elif defined(USE_NO_SEND_PWM) +# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN) + digitalWriteFast(sendPin, LOW); // prepare for all next active states. + pinModeFast(sendPin, INPUT);// inactive state for open drain +# elif defined(USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN) + digitalWriteFast(sendPin, LOW); // Set output to inactive low. +# else + digitalWriteFast(sendPin, HIGH); // Set output to inactive high. +# endif +#else +# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) +# if defined(OUTPUT_OPEN_DRAIN) + digitalWriteFast(sendPin, HIGH); // Set output to inactive high. +# else + pinModeFast(sendPin, INPUT); // inactive state to mimic open drain +# endif +# else + digitalWriteFast(sendPin, LOW); +# endif +#endif + +#if !defined(NO_LED_SEND_FEEDBACK_CODE) + if (FeedbackLEDControl.LedFeedbackEnabled & LED_FEEDBACK_ENABLED_FOR_SEND) { + setFeedbackLED(false); + } +#endif +} + +/** + * Sends an IR space for the specified number of microseconds. + * A space is "no output", so just wait. + */ +void IRsend::space(uint16_t aSpaceMicros) { + customDelayMicroseconds(aSpaceMicros); +} + +/** + * Custom delay function that circumvents Arduino's delayMicroseconds 16 bit limit + * and is (mostly) not extended by the duration of interrupt codes like the millis() interrupt + */ +void IRsend::customDelayMicroseconds(unsigned long aMicroseconds) { +#if defined(ESP32) || defined(ESP8266) + // from https://github.com/crankyoldgit/IRremoteESP8266/blob/00b27cc7ea2e7ac1e48e91740723c805a38728e0/src/IRsend.cpp#L123 + // Invoke a delay(), where possible, to avoid triggering the WDT. + // see https://github.com/Arduino-IRremote/Arduino-IRremote/issues/1114 for the reason of checking for > 16383) + // delayMicroseconds() is only accurate to 16383 us. Ref: https://www.arduino.cc/en/Reference/delayMicroseconds + if (aMicroseconds > 16383) { + delay(aMicroseconds / 1000UL); // Delay for as many whole milliseconds as we can. + // Delay the remaining sub-millisecond. + delayMicroseconds(static_cast(aMicroseconds % 1000UL)); + } else { + delayMicroseconds(aMicroseconds); + } +#else + +# if defined(__AVR__) + unsigned long start = micros() - (64 / clockCyclesPerMicrosecond()); // - (64 / clockCyclesPerMicrosecond()) for reduced resolution and additional overhead +# else + unsigned long start = micros(); +# endif +// overflow invariant comparison :-) + while (micros() - start < aMicroseconds) { + } +#endif +} + +/** + * Enables IR output. The kHz value controls the modulation frequency in kilohertz. + * IF PWM should be generated by a timer, it uses the platform specific timerConfigForSend() function, + * otherwise it computes the delays used by the mark() function. + * If IR_SEND_PIN is defined, maximum PWM frequency for an AVR @16 MHz is 170 kHz (180 kHz if NO_LED_SEND_FEEDBACK_CODE is defined) + */ +void IRsend::enableIROut(uint_fast8_t aFrequencyKHz) { +#if defined(SEND_PWM_BY_TIMER) + timerConfigForSend(aFrequencyKHz); // must set output pin mode and disable receive interrupt if required, e.g. uses the same resource + +#elif defined(USE_NO_SEND_PWM) + (void) aFrequencyKHz; + +#else + periodTimeMicros = (1000U + (aFrequencyKHz / 2)) / aFrequencyKHz; // rounded value -> 26 for 38.46 kHz, 27 for 37.04 kHz, 25 for 40 kHz. +# if defined(IR_SEND_PIN) + periodOnTimeMicros = (((periodTimeMicros * IR_SEND_DUTY_CYCLE_PERCENT) + 50) / 100U); // +50 for rounding -> 830/100 for 30% and 16 MHz +# else +// Heuristics! We require a nanosecond correction for "slow" digitalWrite() functions + periodOnTimeMicros = (((periodTimeMicros * IR_SEND_DUTY_CYCLE_PERCENT) + 50 - (PULSE_CORRECTION_NANOS / 10)) / 100U); // +50 for rounding -> 530/100 for 30% and 16 MHz +# endif +#endif // defined(SEND_PWM_BY_TIMER) + +#if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && defined(OUTPUT_OPEN_DRAIN) // the mode INPUT for mimicking open drain is set at IRLedOff() +# if defined(IR_SEND_PIN) + pinModeFast(IR_SEND_PIN, OUTPUT_OPEN_DRAIN); +# else + pinModeFast(sendPin, OUTPUT_OPEN_DRAIN); +# endif +#else + +// For Non AVR platforms pin mode for SEND_PWM_BY_TIMER must be handled by the timerConfigForSend() function +// because ESP 2.0.2 ledcWrite does not work if pin mode is set, and RP2040 requires gpio_set_function(IR_SEND_PIN, GPIO_FUNC_PWM); +# if defined(__AVR__) || !defined(SEND_PWM_BY_TIMER) +# if defined(IR_SEND_PIN) + pinModeFast(IR_SEND_PIN, OUTPUT); +# else + if (__builtin_constant_p(sendPin)) { + pinModeFast(sendPin, OUTPUT); + } else { + pinMode(sendPin, OUTPUT); + } +# endif +# endif +#endif // defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) +} + +#if defined(SEND_PWM_BY_TIMER) +// Used for Bang&Olufsen +void IRsend::enableHighFrequencyIROut(uint_fast16_t aFrequencyKHz) { + timerConfigForSend(aFrequencyKHz); // must set output pin mode and disable receive interrupt if required, e.g. uses the same resource + // For Non AVR platforms pin mode for SEND_PWM_BY_TIMER must be handled by the timerConfigForSend() function + // because ESP 2.0.2 ledcWrite does not work if pin mode is set, and RP2040 requires gpio_set_function(IR_SEND_PIN, GPIO_FUNC_PWM); +# if defined(__AVR__) +# if defined(IR_SEND_PIN) + pinModeFast(IR_SEND_PIN, OUTPUT); +# else + pinModeFast(sendPin, OUTPUT); +# endif +# endif +} +#endif + +uint16_t IRsend::getPulseCorrectionNanos() { + return PULSE_CORRECTION_NANOS; +} + +/** @}*/ +#if defined(_IR_MEASURE_TIMING) +#undef _IR_MEASURE_TIMING +#endif +#if defined(LOCAL_TRACE) +#undef LOCAL_TRACE +#endif +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_SEND_HPP diff --git a/src/IRVersion.h b/src/IRVersion.h new file mode 100644 index 000000000..acbafa22c --- /dev/null +++ b/src/IRVersion.h @@ -0,0 +1,51 @@ +/** + * @file IRversion.hpp + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2015-2025 Ken Shirriff http://www.righto.com, Rafi Khan, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + * + * For Ken Shiriffs original blog entry, see http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html + * Initially influenced by: + * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 + * and http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ + */ + +#ifndef _IR_VERSION_HPP +#define _IR_VERSION_HPP + +#define VERSION_IRREMOTE "4.4.2" +#define VERSION_IRREMOTE_MAJOR 4 +#define VERSION_IRREMOTE_MINOR 4 +#define VERSION_IRREMOTE_PATCH 2 + +/* + * Macro to convert 3 version parts into an integer + * To be used in preprocessor comparisons, such as #if VERSION_IRREMOTE_HEX >= VERSION_HEX_VALUE(3, 7, 0) + */ +#define VERSION_HEX_VALUE(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +#define VERSION_IRREMOTE_HEX VERSION_HEX_VALUE(VERSION_IRREMOTE_MAJOR, VERSION_IRREMOTE_MINOR, VERSION_IRREMOTE_PATCH) + +#endif // _IR_VERSION_HPP diff --git a/src/IRremote.cpp b/src/IRremote.cpp deleted file mode 100644 index 108a07e3c..000000000 --- a/src/IRremote.cpp +++ /dev/null @@ -1,204 +0,0 @@ -//****************************************************************************** -// IRremote -// Version 2.0.1 June, 2015 -// Initially coded 2009 Ken Shirriff http://www.righto.com -// -// Modified by Paul Stoffregen to support other boards and timers -// Modified by Mitra Ardron -// Added Sanyo and Mitsubishi controllers -// Modified Sony to spot the repeat codes that some Sony's send -// -// Interrupt code based on NECIRrcv by Joe Knapp -// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 -// Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ -// -// JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) -// LG added by Darryl Smith (based on the JVC protocol) -// Whynter A/C ARC-110WD added by Francesco Meschia -//****************************************************************************** - -#include "IRremote.h" - -struct irparams_struct irparams; // the irparams instance - -//+============================================================================= -// The match functions were (apparently) originally MACROs to improve code speed -// (although this would have bloated the code) hence the names being CAPS -// A later release implemented debug output and so they needed to be converted -// to functions. -// I tried to implement a dual-compile mode (DEBUG/non-DEBUG) but for some -// reason, no matter what I did I could not get them to function as macros again. -// I have found a *lot* of bugs in the Arduino compiler over the last few weeks, -// and I am currently assuming that one of these bugs is my problem. -// I may revisit this code at a later date and look at the assembler produced -// in a hope of finding out what is going on, but for now they will remain as -// functions even in non-DEBUG mode -// -int MATCH(unsigned int measured, unsigned int desired) { -#if DEBUG - Serial.print(F("Testing: ")); - Serial.print(TICKS_LOW(desired), DEC); - Serial.print(F(" <= ")); - Serial.print(measured, DEC); - Serial.print(F(" <= ")); - Serial.print(TICKS_HIGH(desired), DEC); -#endif - bool passed = ((measured >= TICKS_LOW(desired)) && (measured <= TICKS_HIGH(desired))); -#if DEBUG - if (passed) { - Serial.println(F("?; passed")); - } else { - Serial.println(F("?; FAILED")); - } -#endif - return passed; -} - -//+======================================================== -// Due to sensor lag, when received, Marks tend to be 100us too long -// -int MATCH_MARK(uint16_t measured_ticks, unsigned int desired_us) { -#if DEBUG - Serial.print(F("Testing mark (actual vs desired): ")); - Serial.print(measured_ticks * MICROS_PER_TICK, DEC); - Serial.print(F("us vs ")); - Serial.print(desired_us, DEC); - Serial.print(F("us: ")); - Serial.print(TICKS_LOW(desired_us + MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); - Serial.print(F(" <= ")); - Serial.print(measured_ticks * MICROS_PER_TICK, DEC); - Serial.print(F(" <= ")); - Serial.print(TICKS_HIGH(desired_us + MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); -#endif - // compensate for marks exceeded by demodulator hardware - bool passed = ((measured_ticks >= TICKS_LOW(desired_us + MARK_EXCESS_MICROS)) - && (measured_ticks <= TICKS_HIGH(desired_us + MARK_EXCESS_MICROS))); -#if DEBUG - if (passed) { - Serial.println(F("?; passed")); - } else { - Serial.println(F("?; FAILED")); - } -#endif - return passed; -} - -//+======================================================== -// Due to sensor lag, when received, Spaces tend to be 100us too short -// -int MATCH_SPACE(uint16_t measured_ticks, unsigned int desired_us) { -#if DEBUG - Serial.print(F("Testing space (actual vs desired): ")); - Serial.print(measured_ticks * MICROS_PER_TICK, DEC); - Serial.print(F("us vs ")); - Serial.print(desired_us, DEC); - Serial.print(F("us: ")); - Serial.print(TICKS_LOW(desired_us - MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); - Serial.print(F(" <= ")); - Serial.print(measured_ticks * MICROS_PER_TICK, DEC); - Serial.print(F(" <= ")); - Serial.print(TICKS_HIGH(desired_us - MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); -#endif - // compensate for marks exceeded and spaces shortened by demodulator hardware - bool passed = ((measured_ticks >= TICKS_LOW(desired_us - MARK_EXCESS_MICROS)) - && (measured_ticks <= TICKS_HIGH(desired_us - MARK_EXCESS_MICROS))); -#if DEBUG - if (passed) { - Serial.println(F("?; passed")); - } else { - Serial.println(F("?; FAILED")); - } -#endif - return passed; -} - -//+============================================================================= -// Interrupt Service Routine - Fires every 50uS -// TIMER2 interrupt code to collect raw data. -// Widths of alternating SPACE, MARK are recorded in rawbuf. -// Recorded in ticks of 50uS [microseconds, 0.000050 seconds] -// 'rawlen' counts the number of entries recorded so far. -// First entry is the SPACE between transmissions. -// As soon as a the first [SPACE] entry gets long: -// Ready is set; State switches to IDLE; Timing of SPACE continues. -// As soon as first MARK arrives: -// Gap width is recorded; Ready is cleared; New logging starts -// -ISR (TIMER_INTR_NAME) { - TIMER_RESET_INTR_PENDING; // reset timer interrupt flag if required (currently only for Teensy and ATmega4809) - - // Read if IR Receiver -> SPACE [xmt LED off] or a MARK [xmt LED on] - // digitalRead() is very slow. Optimisation is possible, but makes the code unportable - uint8_t irdata = (uint8_t) digitalRead(irparams.recvpin); - - irparams.timer++; // One more 50uS tick - if (irparams.rawlen >= RAW_BUFFER_LENGTH) { - // Flag up a read overflow; Stop the State Machine - irparams.overflow = true; - irparams.rcvstate = IR_REC_STATE_STOP; - } - - /* - * Due to a ESP32 compiler bug https://github.com/espressif/esp-idf/issues/1552 no switch statements are possible for ESP32 - * So we change the code to if / else if - */ -// switch (irparams.rcvstate) { - //...................................................................... - if (irparams.rcvstate == IR_REC_STATE_IDLE) { // In the middle of a gap - if (irdata == MARK) { - if (irparams.timer < GAP_TICKS) { // Not big enough to be a gap. - irparams.timer = 0; - } else { - // Gap just ended; Record gap duration; Start recording transmission - // Initialize all state machine variables - irparams.overflow = false; - irparams.rawlen = 0; - irparams.rawbuf[irparams.rawlen++] = irparams.timer; - irparams.timer = 0; - irparams.rcvstate = IR_REC_STATE_MARK; - } - } - } else if (irparams.rcvstate == IR_REC_STATE_MARK) { // Timing Mark - if (irdata == SPACE) { // Mark ended; Record time - irparams.rawbuf[irparams.rawlen++] = irparams.timer; - irparams.timer = 0; - irparams.rcvstate = IR_REC_STATE_SPACE; - } - } else if (irparams.rcvstate == IR_REC_STATE_SPACE) { // Timing Space - if (irdata == MARK) { // Space just ended; Record time - irparams.rawbuf[irparams.rawlen++] = irparams.timer; - irparams.timer = 0; - irparams.rcvstate = IR_REC_STATE_MARK; - - } else if (irparams.timer > GAP_TICKS) { // Space - // A long Space, indicates gap between codes - // Flag the current code as ready for processing - // Switch to STOP - // Don't reset timer; keep counting Space width - irparams.rcvstate = IR_REC_STATE_STOP; - } - } else if (irparams.rcvstate == IR_REC_STATE_STOP) { // Waiting; Measuring Gap - if (irdata == MARK) { - irparams.timer = 0; // Reset gap timer - } - } - -#ifdef BLINKLED - // If requested, flash LED while receiving IR data - if (irparams.blinkflag) { - if (irdata == MARK) { - if (irparams.blinkpin) { - digitalWrite(irparams.blinkpin, HIGH); // Turn user defined pin LED on - } else { - BLINKLED_ON(); // if no user defined LED pin, turn default LED pin for the hardware on - } - } else { - if (irparams.blinkpin) { - digitalWrite(irparams.blinkpin, LOW); // Turn user defined pin LED on - } else { - BLINKLED_OFF(); // if no user defined LED pin, turn default LED pin for the hardware on - } - } - } -#endif // BLINKLED -} diff --git a/src/IRremote.h b/src/IRremote.h index 2c0686997..73cae0385 100644 --- a/src/IRremote.h +++ b/src/IRremote.h @@ -1,576 +1,58 @@ /** * @file IRremote.h - * @brief Public API to the library. + * + * @brief Stub for backward compatibility */ -//****************************************************************************** -// IRremote -// Version 2.0.1 June, 2015 -// Initially coded 2009 Ken Shirriff http://www.righto.com -// Edited by Mitra to add new controller SANYO -// -// Interrupt code based on NECIRrcv by Joe Knapp -// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 -// Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ -// -// JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) -// LG added by Darryl Smith (based on the JVC protocol) -// Whynter A/C ARC-110WD added by Francesco Meschia -// MagiQuest added by E. Stuart Hicks (based on code by mpflaga - https://github.com/mpflaga/Arduino-IRremote/) -//****************************************************************************** #ifndef IRremote_h #define IRremote_h -//------------------------------------------------------------------------------ -#include "private/IRremoteInt.h" - -/**************************************************** - * PROTOCOLS - ****************************************************/ -//------------------------------------------------------------------------------ -// Supported IR protocols -// Each protocol you include costs memory and, during decode, costs time -// Disable (set to 0) all the protocols you do not need/want! -// -//#define DECODE_AIWA_RC_T501 1 -//#define SEND_AIWA_RC_T501 1 - -#define DECODE_BOSEWAVE 1 -#define SEND_BOSEWAVE 1 - -#define DECODE_DENON 1 -#define SEND_DENON 1 - -#define DECODE_DISH 0 // NOT WRITTEN -#define SEND_DISH 1 - -#define DECODE_JVC 1 -#define SEND_JVC 1 - -#define DECODE_LEGO_PF 1 -#define SEND_LEGO_PF 1 - -#define DECODE_LG 1 -#define SEND_LG 1 - -#define DECODE_MAGIQUEST 1 -#define SEND_MAGIQUEST 1 - -//#define DECODE_MITSUBISHI 1 // Faulty implementation -//#define SEND_MITSUBISHI 0 // NOT WRITTEN - -//#define USE_NEC_STANDARD // remove comment to have the standard NEC decoding (LSB first) available. -#if defined(USE_NEC_STANDARD) -#define DECODE_NEC_STANDARD 1 -#define DECODE_NEC 0 -#define LSB_FIRST_REQUIRED -#else -#define DECODE_NEC_STANDARD 0 -#define DECODE_NEC 1 -#endif -#define SEND_NEC 1 -#define SEND_NEC_STANDARD 1 - -#define DECODE_PANASONIC 1 -#define SEND_PANASONIC 1 - -#define DECODE_RC5 1 -#define SEND_RC5 1 - -#define DECODE_RC6 1 -#define SEND_RC6 1 - -#define DECODE_SAMSUNG 1 -#define SEND_SAMSUNG 1 - -#define DECODE_SANYO 1 -#define SEND_SANYO 0 // NOT WRITTEN - -#define DECODE_SHARP 1 -#define SEND_SHARP 1 - -#define DECODE_SHARP_ALT 1 -#define SEND_SHARP_ALT 1 -#if SEND_SHARP_ALT -#define LSB_FIRST_REQUIRED -#endif - -#define DECODE_SONY 1 -#define SEND_SONY 1 - -#define DECODE_WHYNTER 1 -#define SEND_WHYNTER 1 - -#define DECODE_HASH 1 // special decoder for all protocols - -/** - * An enum consisting of all supported formats. - * You do NOT need to remove entries from this list when disabling protocols! - */ -typedef enum { - UNKNOWN = -1, - UNUSED = 0, -// AIWA_RC_T501, - BOSEWAVE, - DENON, - DISH, - JVC, - LEGO_PF, - LG, - MAGIQUEST, -// MITSUBISHI, - NEC_STANDARD, - NEC, - PANASONIC, - RC5, - RC6, - SAMSUNG, - SANYO, - SHARP, - SHARP_ALT, - SONY, - WHYNTER, -} decode_type_t; - -/** - * Comment this out for lots of lovely debug output. - */ -//#define DEBUG -//------------------------------------------------------------------------------ -// Debug directives -// -#ifdef DEBUG -# define DBG_PRINT(...) Serial.print(__VA_ARGS__) -# define DBG_PRINTLN(...) Serial.println(__VA_ARGS__) -#else -/** - * If DEBUG, print the arguments, otherwise do nothing. - */ -# define DBG_PRINT(...) void() -/** - * If DEBUG, print the arguments as a line, otherwise do nothing. - */ -# define DBG_PRINTLN(...) void() -#endif - -//------------------------------------------------------------------------------ -// Helper macro for getting a macro definition as string -// -#define STR_HELPER(x) #x -#define STR(x) STR_HELPER(x) - -//------------------------------------------------------------------------------ -// Mark & Space matching functions -// -int MATCH(unsigned int measured, unsigned int desired); -int MATCH_MARK(uint16_t measured_ticks, unsigned int desired_us); -int MATCH_SPACE(uint16_t measured_ticks, unsigned int desired_us); - -/**************************************************** - * RECEIVING - ****************************************************/ -/** - * Results returned from the decoder - */ -struct decode_results { - decode_type_t decode_type; ///< UNKNOWN, NEC, SONY, RC5, ... - uint16_t address; ///< Used by Panasonic & Sharp6 NEC_standard [16-bits] - uint32_t value; ///< Decoded value / command [max 32-bits] - uint16_t bits; ///< Number of bits in decoded value - uint16_t magnitude; ///< Used by MagiQuest [16-bits] - bool isRepeat; ///< True if repeat of value is detected - - // next 3 values are copies of irparams values - see IRremoteint.h - uint16_t *rawbuf; ///< Raw intervals in 50uS ticks - uint16_t rawlen; ///< Number of records in rawbuf - bool overflow; ///< true if IR raw code too long -}; - -/** - * DEPRECATED - * Decoded value for NEC and others when a repeat code is received - * Use Flag decode_results.isRepeat (see above) instead - */ -#define REPEAT 0xFFFFFFFF - -/** - * Main class for receiving IR - */ -class IRrecv { -public: - /** - * Instantiate the IRrecv class. Multiple instantiation is not supported. - * @param recvpin Arduino pin to use. No sanity check is made. - */ - IRrecv(int recvpin); - /** - * Instantiate the IRrecv class. Multiple instantiation is not supported. - * @param recvpin Arduino pin to use, where a demodulating IR receiver is connected. - * @param blinkpin pin to blink when receiving IR. Not supported by all hardware. No sanity check is made. - */ - IRrecv(int recvpin, int blinkpin); - - /** - * TODO: Why is this public??? - * @param blinkflag - */ - void blink13(int blinkflag); - - /** - * Attempt to decode the recently receive IR signal - * @param results decode_results instance returning the decode, if any. - * @return success of operation. - */ - bool decode(decode_results *aResults);__attribute__ ((deprecated ("You should use decode() without a parameter."))) - ; // deprecated - bool decode(); - - /** - * Enable IR reception. - */ - void enableIRIn(); - - /** - * Disable IR reception. - */ - void disableIRIn(); - - /** - * Returns status of reception - * @return true if no reception is on-going. - */ - bool isIdle(); - - /** - * Returns status of reception and copies IR-data to decode_results buffer if true. - * @return true if data is available. - */ - bool available(); - - /** - * Called to re-enable IR reception. - */ - void resume(); - - const char* getProtocolString(); - void printResultShort(Print *aSerial); - void printIRResultRaw(Print *aSerial); - void printIRResultRawFormatted(Print *aSerial); - void printIRResultAsCArray(Print *aSerial); - void printIRResultAsCVariables(Print *aSerial); - /** - * Print the result (second argument) as Pronto Hex on the Stream supplied as argument. - * @param stream The Stream on which to write, often Serial - * @param results the decode_results as delivered from irrecv.decode. - * @param frequency Modulation frequency in Hz. Often 38000Hz. - */ - void dumpPronto(Print *aSerial, unsigned int frequency = 38000U); - void printIRResultAsPronto(Print *aSerial, unsigned int frequency = 38000U); - - bool decodePulseDistanceData(uint8_t aNumberOfBits, uint8_t aStartOffset, unsigned int aBitMarkMicros, - unsigned int aOneSpaceMicros, unsigned int aZeroSpaceMicros, bool aMSBfirst = true); - - decode_results results; // the instance for decoding - -private: -#if DECODE_HASH - bool decodeHash(); - bool decodeHash(decode_results *aResults); - int compare(unsigned int oldval, unsigned int newval); -#endif - - //...................................................................... -#if DECODE_RC5 - /** - * Try to decode the recently received IR signal as an RC5 signal- - * @param results decode_results instance returning the decode, if any. - * @return Success of the operation. - */ - bool decodeRC5(); - bool decodeRC5(decode_results *aResults); -#endif -#if DECODE_RC6 - bool decodeRC6(); - bool decodeRC6(decode_results *aResults); -#endif - //...................................................................... -#if DECODE_NEC - bool decodeNEC(); - bool decodeNEC(decode_results *aResults); -#endif -#if DECODE_NEC_STANDARD - bool decodeNECStandard(); -#endif - - //...................................................................... -#if DECODE_SONY - bool decodeSony(); - bool decodeSony(decode_results *aResults); -#endif - //...................................................................... -#if DECODE_PANASONIC - bool decodePanasonic(); - bool decodePanasonic(decode_results *aResults); -#endif - //...................................................................... -#if DECODE_JVC - bool decodeJVC(); - bool decodeJVC(decode_results *aResults); -#endif - //...................................................................... -#if DECODE_SAMSUNG - bool decodeSAMSUNG(); - bool decodeSAMSUNG(decode_results *aResults); -#endif - //...................................................................... -#if DECODE_WHYNTER - bool decodeWhynter(); - bool decodeWhynter(decode_results *aResults); -#endif - //...................................................................... -//#if DECODE_AIWA_RC_T501 -// bool decodeAiwaRCT501(); -// bool decodeAiwaRCT501(decode_results *aResults); -//#endif - //...................................................................... -#if DECODE_LG - bool decodeLG(); - bool decodeLG(decode_results *aResults); -#endif - //...................................................................... -#if DECODE_SANYO - bool decodeSanyo(); - bool decodeSanyo(decode_results *aResults); -#endif - //...................................................................... -//#if DECODE_MITSUBISHI -// bool decodeMitsubishi(); -// bool decodeMitsubishi(decode_results *aResults); -//#endif - //...................................................................... -#if DECODE_DISH - bool decodeDish () ; // NOT WRITTEN -#endif - //...................................................................... -#if DECODE_SHARP - bool decodeSharp(); - bool decodeSharp(decode_results *aResults); -#endif -#if DECODE_SHARP_ALT - bool decodeSharpAlt(); - bool decodeSharpAlt(decode_results *aResults); -#endif - //...................................................................... -#if DECODE_DENON - bool decodeDenon(); - bool decodeDenon(decode_results *aResults); -#endif - //...................................................................... -#if DECODE_LEGO_PF - bool decodeLegoPowerFunctions () ; -#endif - //...................................................................... -#if DECODE_BOSEWAVE - bool decodeBoseWave(); - bool decodeBoseWave(decode_results *aResults); -#endif - //...................................................................... -#if DECODE_MAGIQUEST - bool decodeMagiQuest(); - bool decodeMagiQuest(decode_results *aResults); -#endif -}; - -/**************************************************** - * SENDING - ****************************************************/ -/** - * Define to use no carrier PWM, just simulate an active low receiver signal. - */ -//#define USE_NO_SEND_PWM -/** - * Define to use carrier PWM generation in software, instead of hardware PWM. - */ -//#define USE_SOFT_SEND_PWM -/** - * If USE_SOFT_SEND_PWM, this amount is subtracted from the on-time of the pulses. - */ -#ifndef PULSE_CORRECTION_MICROS -#define PULSE_CORRECTION_MICROS 3 -#endif -/** - * If USE_SOFT_SEND_PWM, use spin wait instead of delayMicros(). - */ -//#define USE_SPIN_WAIT -/** - * Main class for sending IR - */ -class IRsend { -public: -#if defined(USE_SOFT_SEND_PWM) || defined(USE_NO_SEND_PWM) - IRsend(int pin = IR_SEND_PIN) { - sendPin = pin; - } -#else - IRsend() { +#include "IRremote.hpp" + +#warning It seems, that you are using an old version 2.0 code / example. +#warning This version is no longer supported! +#warning Upgrade instructions can be found here: "https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#converting-your-2x-program-to-the-4x-version" +#warning Please use one of the new code examples from the library, available at "File > Examples > Examples from Custom Libraries / IRremote". +#warning Start with the SimpleReceiver or SimpleSender example. +#warning The examples are documented here: "https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#examples-for-this-library" +#warning Or just downgrade your library to version 2.6.0. + +/********************************************************************************************************************** + * The OLD and DEPRECATED decode function with parameter aResults, kept for backward compatibility to old 2.0 tutorials + * This function calls the old MSB first decoders and fills only the 3 variables: + * aResults->value + * aResults->bits + * aResults->decode_type + * It prints a message on the first call. + **********************************************************************************************************************/ +bool IRrecv::decode(decode_results *aResults) { + static bool sMessageWasSent = false; + if (!sMessageWasSent) { + Serial.println(F("**************************************************************************************************")); + Serial.println(F("Thank you for using the IRremote library!")); + Serial.println(F("It seems, that you are using an old version 2.0 code / example.")); + Serial.println(F("This version is no longer supported!")); + Serial.println(); + Serial.println(F("Upgrade instructions can be found here:")); + Serial.println(F(" https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#converting-your-2x-program-to-the-4x-version")); + Serial.println(); + Serial.println(F("Please use one of the new code examples from the library,")); + Serial.println(F(" available at \"File > Examples > Examples from Custom Libraries / IRremote\".")); + Serial.println(F("Start with the SimpleReceiver or SimpleSender example.")); + Serial.println(F("The examples are documented here:")); + Serial.println(F(" https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#examples-for-this-library")); + Serial.println(); + Serial.println(F("Or just downgrade your library to version 2.6.0.")); + Serial.println(); + Serial.println(F("Thanks")); + Serial.println(F("**************************************************************************************************")); + Serial.println(); + Serial.println(); + sMessageWasSent = true; } -#endif - - void custom_delay_usec(unsigned long uSecs); - void enableIROut(int khz); - void sendPulseDistanceWidthData(unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros, - unsigned int aZeroSpaceMicros, unsigned long aData, uint8_t aNumberOfBits, bool aMSBfirst = true); - void mark(uint16_t timeMicros); - void mark_long(uint32_t timeMicros); - void space(uint16_t timeMicros); - void space_long(uint32_t timeMicros); - void sendRaw(const unsigned int buf[], unsigned int len, unsigned int hz); - void sendRaw_P(const unsigned int buf[], unsigned int len, unsigned int hz); - - //...................................................................... -#if SEND_RC5 - void sendRC5(uint32_t data, uint8_t nbits); - void sendRC5ext(uint8_t addr, uint8_t cmd, boolean toggle); -#endif -#if SEND_RC6 - void sendRC6(uint32_t data, uint8_t nbits); - void sendRC6(uint64_t data, uint8_t nbits); -#endif - //...................................................................... -#if SEND_NEC || SEND_NEC_STANDARD - void sendNECRepeat(); -#endif -#if SEND_NEC - void sendNEC(uint32_t data, uint8_t nbits, bool repeat = false); -#endif -#if SEND_NEC_STANDARD - void sendNECStandard(uint16_t aAddress, uint8_t aCommand, uint8_t aNumberOfRepeats = 0); -#endif - //...................................................................... -#if SEND_SONY - void sendSony(unsigned long data, int nbits); -#endif - //...................................................................... -#if SEND_PANASONIC - void sendPanasonic(unsigned int address, unsigned long data); -#endif - //...................................................................... -#if SEND_JVC - // JVC does NOT repeat by sending a separate code (like NEC does). - // The JVC protocol repeats by skipping the header. - // To send a JVC repeat signal, send the original code value - // and set 'repeat' to true - void sendJVC(unsigned long data, int nbits, bool repeat = false); -#endif - //...................................................................... -#if SEND_SAMSUNG - void sendSAMSUNG(unsigned long data, int nbits); -#endif - //...................................................................... -#if SEND_WHYNTER - void sendWhynter(unsigned long data, int nbits); -#endif - //...................................................................... -//#if SEND_AIWA_RC_T501 -// void sendAiwaRCT501(int code); -//#endif - //...................................................................... -#if SEND_LG - void sendLG(unsigned long data, int nbits); -#endif - //...................................................................... -#if SEND_SANYO - void sendSanyo ( ) ; // NOT WRITTEN -#endif - //...................................................................... -//#if SEND_MISUBISHI -// void sendMitsubishi ( ) ; // NOT WRITTEN -//#endif - //...................................................................... -#if SEND_DISH - void sendDISH(unsigned long data, int nbits); -#endif - //...................................................................... -#if SEND_SHARP - void sendSharpRaw(unsigned long data, int nbits); - void sendSharp(unsigned int address, unsigned int command); -#endif -#if SEND_SHARP_ALT - void sendSharpAltRaw(unsigned int data, int nbits); - void sendSharpAlt(uint8_t address, uint8_t command); -#endif - //...................................................................... -#if SEND_DENON - void sendDenon(unsigned long data, int nbits); -#endif - //...................................................................... -#if SEND_LEGO_PF - void sendLegoPowerFunctions(uint16_t data, bool repeat = true); -#endif - //...................................................................... -#if SEND_BOSEWAVE - void sendBoseWave(unsigned char code); -#endif - //...................................................................... -#if SEND_MAGIQUEST - void sendMagiQuest(unsigned long wand_id, unsigned int magnitude); -#endif - - /** - * Parse the string given as Pronto Hex, and send it a number of times given - * as the second argument. Thereby the division of the Pronto Hex into - * an intro-sequence and a repeat sequence is taken into account: - * First the intro sequence is sent, then the repeat sequence is sent times-1 times. - * However, if the intro sequence is empty, the repeat sequence is sent times times. - * Reference. - * - * Note: Using this function is very wasteful for the memory consumption on - * a small board. - * Normally it is a much better ide to use a tool like e.g. IrScrutinizer - * to transform Pronto type signals offline - * to a more memory efficient format. - * - * @param prontoHexString C type string (null terminated) containing a Pronto Hex representation. - * @param times Number of times to send the signal. - */ - void sendPronto(const char* prontoHexString, unsigned int times = 1U); - - void sendPronto(const uint16_t* data, unsigned int length, unsigned int times = 1U); - -#if HAS_FLASH_READ || defined(DOXYGEN) - void sendPronto_PF(uint_farptr_t str, unsigned int times = 1U); - - /** - * Version of sendPronto that reads from PROGMEM, saving RAM memory. - * @param pronto C type string (null terminated) containing a Pronto Hex representation. - * @param times Number of times to send the signal. - */ - void sendPronto_PF(const char *str, unsigned int times = 1U); - void sendPronto(const __FlashStringHelper *str, unsigned int times = 1U); -#endif - -private: -#if (DECODE_RC5 || DECODE_RC6) - /** - * This helper function is shared by RC5 and RC6 - */ - int getRClevel(decode_results *results, unsigned int *offset, int *used, int t1); -#endif - -#if defined(USE_SOFT_SEND_PWM) || defined(USE_NO_SEND_PWM) - int sendPin; - -# if defined(USE_SOFT_SEND_PWM) - unsigned int periodTimeMicros; - unsigned int periodOnTimeMicros; - - void sleepMicros(unsigned long us); - void sleepUntilMicros(unsigned long targetTime); -# endif - -#else - const int sendPin = IR_SEND_PIN; -#endif -}; + return decode_old(aResults); +} #endif // IRremote_h +#pragma once + diff --git a/src/IRremote.hpp b/src/IRremote.hpp new file mode 100644 index 000000000..fdeeb8350 --- /dev/null +++ b/src/IRremote.hpp @@ -0,0 +1,334 @@ +/** + * @file IRremote.hpp + * + * @brief Public API to the library. + * + * @code + * !!! All the macro values defined here can be overwritten with values, !!! + * !!! the user defines in its source code BEFORE the #include !!! + * @endcode + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2015-2025 Ken Shirriff http://www.righto.com, Rafi Khan, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + * + * For Ken Shiriffs original blog entry, see http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html + * Initially influenced by: + * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 + * and http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ + */ + +/* + * This library can be configured at compile time by the following options / macros: + * For more details see: https://github.com/Arduino-IRremote/Arduino-IRremote#compile-options--macros-for-this-library + * + * - RAW_BUFFER_LENGTH Buffer size of raw input buffer. Must be even! 100 is sufficient for *regular* protocols of up to 48 bits. + * - IR_SEND_PIN If specified (as constant), reduces program size and improves send timing for AVR. + * - SEND_PWM_BY_TIMER Disable carrier PWM generation in software and use (restricted) hardware PWM. + * - USE_NO_SEND_PWM Use no carrier PWM, just simulate an **active low** receiver signal. Overrides SEND_PWM_BY_TIMER definition. + * - USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN Use or simulate open drain output mode at send pin. Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin! + * - EXCLUDE_EXOTIC_PROTOCOLS If activated, BANG_OLUFSEN, BOSEWAVE, WHYNTER, FAST and LEGO_PF are excluded in decode() and in sending with IrSender.write(). + * - EXCLUDE_UNIVERSAL_PROTOCOLS If activated, the universal decoder for pulse distance protocols and decodeHash (special decoder for all protocols) are excluded in decode(). + * - DECODE_* Selection of individual protocols to be decoded. See below. + * - MARK_EXCESS_MICROS Value is subtracted from all marks and added to all spaces before decoding, to compensate for the signal forming of different IR receiver modules. + * - RECORD_GAP_MICROS Minimum gap between IR transmissions, to detect the end of a protocol. + * - FEEDBACK_LED_IS_ACTIVE_LOW Required on some boards (like my BluePill and my ESP8266 board), where the feedback LED is active low. + * - NO_LED_FEEDBACK_CODE This completely disables the LED feedback code for send and receive. + * - NO_LED_RECEIVE_FEEDBACK_CODE This disables the LED feedback code for receive. + * - NO_LED_SEND_FEEDBACK_CODE This disables the LED feedback code for send. + * - IR_INPUT_IS_ACTIVE_HIGH Enable it if you use a RF receiver, which has an active HIGH output signal. + * - IR_SEND_DUTY_CYCLE_PERCENT Duty cycle of IR send signal. + * - MICROS_PER_TICK Resolution of the raw input buffer data. Corresponds to 2 pulses of each 26.3 us at 38 kHz. + * - IR_USE_AVR_TIMER* Selection of timer to be used for generating IR receiving sample interval. + */ + +#ifndef _IR_REMOTE_HPP +#define _IR_REMOTE_HPP + +#include "IRVersion.h" + +// activate it for all cores that does not use the -flto flag, if you get false error messages regarding begin() during compilation. +//#define SUPPRESS_ERROR_MESSAGE_FOR_BEGIN + +/* + * If activated, BANG_OLUFSEN, BOSEWAVE, MAGIQUEST, WHYNTER, FAST and LEGO_PF are excluded in decoding and in sending with IrSender.write + */ +//#define EXCLUDE_EXOTIC_PROTOCOLS +/**************************************************** + * PROTOCOLS + ****************************************************/ +/* + * Supported IR protocols + * Each protocol you include costs memory and, during decode, costs time + * Copy the lines with the protocols you need in your program before the #include line + * See also SimpleReceiver example + */ + +#if !defined(NO_DECODER) // for sending raw only +# if (!(defined(DECODE_DENON) || defined(DECODE_JVC) || defined(DECODE_KASEIKYO) \ +|| defined(DECODE_PANASONIC) || defined(DECODE_LG) || defined(DECODE_NEC) || defined(DECODE_ONKYO) || defined(DECODE_SAMSUNG) \ +|| defined(DECODE_SONY) || defined(DECODE_RC5) || defined(DECODE_RC6) \ +|| defined(DECODE_DISTANCE_WIDTH) || defined(DECODE_HASH) || defined(DECODE_BOSEWAVE) \ +|| defined(DECODE_LEGO_PF) || defined(DECODE_MAGIQUEST) || defined(DECODE_FAST) || defined(DECODE_WHYNTER))) +/* + * If no protocol is explicitly enabled, we enable all protocols + */ +#define DECODE_DENON // Includes Sharp +#define DECODE_JVC +#define DECODE_KASEIKYO +#define DECODE_PANASONIC // alias for DECODE_KASEIKYO +#define DECODE_LG +#define DECODE_NEC // Includes Apple and Onkyo +#define DECODE_SAMSUNG +#define DECODE_SONY +#define DECODE_RC5 +#define DECODE_RC6 + +# if !defined(EXCLUDE_EXOTIC_PROTOCOLS) // saves around 2000 bytes program memory +#define DECODE_BOSEWAVE +#define DECODE_LEGO_PF +#define DECODE_MAGIQUEST +#define DECODE_WHYNTER +#define DECODE_FAST +# endif + +# if !defined(EXCLUDE_UNIVERSAL_PROTOCOLS) +#define DECODE_DISTANCE_WIDTH // universal decoder for pulse distance width protocols - requires up to 750 bytes additional program memory +#define DECODE_HASH // special decoder for all protocols - requires up to 250 bytes additional program memory +# endif +# endif +#endif // !defined(NO_DECODER) + +//#define DECODE_BEO // Bang & Olufsen protocol always must be enabled explicitly. It prevents decoding of SONY! + +#if defined(DECODE_NEC) && !(~(~DECODE_NEC + 0) == 0 && ~(~DECODE_NEC + 1) == 1) +#warning "The macros DECODE_XXX no longer require a value. Decoding is now switched by defining / non defining the macro." +#endif + +//#define DEBUG // Activate this for lots of lovely debug output from the IRremote core. + +/**************************************************** + * RECEIVING + ****************************************************/ +/** + * MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding, + * to compensate for the signal forming of different IR receiver modules + * For Vishay TSOP*, marks tend to be too long and spaces tend to be too short. + * If you set MARK_EXCESS_MICROS to approx. 50us then the TSOP4838 works best. + * At 100us it also worked, but not as well. + * Set MARK_EXCESS to 100us and the VS1838 doesn't work at all. + * + * The right value is critical for IR codes using short pulses like Denon / Sharp / Lego + * + * Observed values: + * Delta of each signal type is around 50 up to 100 and at low signals up to 200. TSOP is better, especially at low IR signal level. + * VS1838 Mark Excess -50 at low intensity to +50 us at high intensity + * TSOP31238 Mark Excess 0 to +50 + */ +#if !defined(MARK_EXCESS_MICROS) +// To change this value, you simply can add a line #define "MARK_EXCESS_MICROS " in your ino file before the line "#include " +#define MARK_EXCESS_MICROS 20 +#endif + +/** + * Minimum gap between IR transmissions, to detect the end of a protocol. + * Must be greater than any space of a protocol e.g. the NEC header space of 4500 us. + * Must be smaller than any gap between a command and a repeat; e.g. the retransmission gap for Sony is around 15 ms for Sony20 protocol. + * Keep in mind, that this is the delay between the end of the received command and the start of decoding. + */ +#if !defined(RECORD_GAP_MICROS) +// To change this value, you simply can add a line #define "RECORD_GAP_MICROS " in your *.ino file before the line "#include " +// Maximum value for RECORD_GAP_MICROS, which fit into 8 bit buffer, using 50 us as tick, is 12750 +#define RECORD_GAP_MICROS 8000 // RECS80 (https://www.mikrocontroller.net/articles/IRMP#RECS80) 1 bit space is 7500µs , NEC header space is 4500 +#endif +/** + * Threshold for warnings at printIRResult*() to report about changing the RECORD_GAP_MICROS value to a higher value. + */ +#if !defined(RECORD_GAP_MICROS_WARNING_THRESHOLD) +// To change this value, you simply can add a line #define "RECORD_GAP_MICROS_WARNING_THRESHOLD " in your *.ino file before the line "#include " +#define RECORD_GAP_MICROS_WARNING_THRESHOLD 15000 +#endif + +/** Minimum gap between IR transmissions, in MICROS_PER_TICK */ +#define RECORD_GAP_TICKS (RECORD_GAP_MICROS / MICROS_PER_TICK) + +/* + * Activate this line if your receiver has an external output driver transistor / "inverted" output + */ +//#define IR_INPUT_IS_ACTIVE_HIGH +#if defined(IR_INPUT_IS_ACTIVE_HIGH) +// IR detector output is active high +#define INPUT_MARK 1 ///< Sensor output for a mark ("flash") +#else +// IR detector output is active low +#define INPUT_MARK 0 ///< Sensor output for a mark ("flash") +#endif +/**************************************************** + * SENDING + ****************************************************/ +/** + * Define to disable carrier PWM generation in software and use (restricted) hardware PWM. + */ +//#define SEND_PWM_BY_TIMER // restricts send pin on many platforms to fixed pin numbers +#if (defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE)) || defined(ARDUINO_ARCH_MBED) +# if !defined(SEND_PWM_BY_TIMER) +#define SEND_PWM_BY_TIMER // the best and default method for ESP32 etc. +#warning INFO: For ESP32, RP2040, mbed and particle boards SEND_PWM_BY_TIMER is enabled by default, since we have the resources and timing is more exact than the software generated one. If this is not intended, deactivate the line in IRremote.hpp over this warning message in file IRremote.hpp. +# endif +#else +# if defined(SEND_PWM_BY_TIMER) +# if defined(IR_SEND_PIN) +#undef IR_SEND_PIN // to avoid warning 3 lines later +#warning Since SEND_PWM_BY_TIMER is defined, the existing value of IR_SEND_PIN is discarded and replaced by the value determined by timer used for PWM generation +# endif +#define IR_SEND_PIN DeterminedByTimer // must be set here, since it is evaluated at IRremoteInt.h, before the include of private/IRTimer.hpp +# endif +#endif + +/** + * Define to use no carrier PWM, just simulate an active low receiver signal. + */ +//#define USE_NO_SEND_PWM +#if defined(SEND_PWM_BY_TIMER) && defined(USE_NO_SEND_PWM) +#warning "SEND_PWM_BY_TIMER and USE_NO_SEND_PWM are both defined -> undefine SEND_PWM_BY_TIMER now!" +#undef SEND_PWM_BY_TIMER // USE_NO_SEND_PWM overrides SEND_PWM_BY_TIMER +#endif + +/** + * Define to use or simulate open drain output mode at send pin. + * Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin! + */ +//#define USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN +#if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN) +#warning Pin mode OUTPUT_OPEN_DRAIN is not supported on this platform -> mimick open drain mode by switching between INPUT and OUTPUT mode. +#endif +/** + * This amount is subtracted from the on-time of the pulses generated for software PWM generation. + * It should be the time used for digitalWrite(sendPin, LOW) and the call to delayMicros() + * Measured value for Nano @16MHz is around 3000, for Bluepill @72MHz is around 700, for Zero 3600 + */ +#if !defined(PULSE_CORRECTION_NANOS) +# if defined(F_CPU) +// To change this value, you simply can add a line #define "PULSE_CORRECTION_NANOS " in your ino file before the line "#include " +#define PULSE_CORRECTION_NANOS (48000L / (F_CPU/MICROS_IN_ONE_SECOND)) // 3000 @16MHz, 666 @72MHz +# else +#define PULSE_CORRECTION_NANOS 600 +# endif +#endif + +/** + * Duty cycle in percent for sent signals. + */ +#if ! defined(IR_SEND_DUTY_CYCLE_PERCENT) +#define IR_SEND_DUTY_CYCLE_PERCENT 30 // 30 saves power and is compatible to the old existing code +#endif + +/** + * microseconds per clock interrupt tick + */ +#if ! defined(MICROS_PER_TICK) +#define MICROS_PER_TICK 50L // must be with L to get 32 bit results if multiplied with rawbuf[] content. +#endif + +#define MILLIS_IN_ONE_SECOND 1000L +#define MICROS_IN_ONE_SECOND 1000000L +#define MICROS_IN_ONE_MILLI 1000L + +#if defined(NO_LED_FEEDBACK_CODE) +# if !defined(NO_LED_RECEIVE_FEEDBACK_CODE) +#define NO_LED_RECEIVE_FEEDBACK_CODE +# endif +# if !defined(NO_LED_SEND_FEEDBACK_CODE) +#define NO_LED_SEND_FEEDBACK_CODE +# endif +#endif + +#include "IRremoteInt.h" +/* + * We always use digitalWriteFast() and digitalReadFast() functions to have a consistent mapping for pins. + * For most non AVR cpu's, it is just a mapping to digitalWrite() and digitalRead() functions. + */ +#if !defined(MEGATINYCORE) // megaTinyCore has it own digitalWriteFast function set, except digitalToggleFast(). +#include "digitalWriteFast.h" +#endif + +#if !defined(USE_IRREMOTE_HPP_AS_PLAIN_INCLUDE) +#include "private/IRTimer.hpp" // defines IR_SEND_PIN for AVR and SEND_PWM_BY_TIMER + +# if !defined(NO_LED_FEEDBACK_CODE) +# if !defined(LED_BUILTIN) +/* + * print a warning + */ +#warning INFO: No definition for LED_BUILTIN found -> default LED feedback is disabled. +# endif +#include "IRFeedbackLED.hpp" +# else +void disableLEDFeedback() {}; // dummy function for examples +# endif + +#include "LongUnion.h" // used in most decoders + +/* + * Include the sources here to enable compilation with macro values set by user program. + */ +#include "IRProtocol.hpp" // must be first, it includes definition for PrintULL (unsigned long long) +#if !defined(DISABLE_CODE_FOR_RECEIVER) +#include "IRReceive.hpp" +#endif +#include "IRSend.hpp" + +/* + * Include the sources of all decoders here to enable compilation with macro values set by user program. + */ +#include "ir_BangOlufsen.hpp" +#include "ir_BoseWave.hpp" +#include "ir_Denon.hpp" +#include "ir_JVC.hpp" +#include "ir_Kaseikyo.hpp" +#include "ir_Lego.hpp" +#include "ir_LG.hpp" +#include "ir_MagiQuest.hpp" +#include "ir_NEC.hpp" +#include "ir_RC5_RC6.hpp" +#include "ir_Samsung.hpp" +#include "ir_Sony.hpp" +#include "ir_FAST.hpp" +#include "ir_Others.hpp" +#include "ir_Pronto.hpp" // pronto is an universal decoder and encoder +# if defined(DECODE_DISTANCE_WIDTH) // universal decoder for pulse distance width protocols - requires up to 750 bytes additional program memory +#include "ir_DistanceWidthProtocol.hpp" +# endif +#endif // #if !defined(USE_IRREMOTE_HPP_AS_PLAIN_INCLUDE) + +/** + * Macros for legacy compatibility + */ +#define RAWBUF 101 // Maximum length of raw duration buffer +#define REPEAT 0xFFFFFFFF +#define USECPERTICK MICROS_PER_TICK +#define MARK_EXCESS MARK_EXCESS_MICROS + +#endif // _IR_REMOTE_HPP diff --git a/src/IRremoteInt.h b/src/IRremoteInt.h new file mode 100644 index 000000000..02023a8df --- /dev/null +++ b/src/IRremoteInt.h @@ -0,0 +1,699 @@ +/** + * @file IRremoteInt.h + * @brief Contains all declarations required for the interface to IRremote. + * Could not be named IRremote.h, since this has another semantic (it must include all *.hpp files) for old example code found in the wild. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2015-2023 Ken Shirriff http://www.righto.com, Rafi Khan, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_REMOTE_INT_H +#define _IR_REMOTE_INT_H + +#include + +#define MARK 1 +#define SPACE 0 + +#if defined(PARTICLE) +#define F_CPU 16000000 // definition for a board for which F_CPU is not defined +#endif +#if defined(F_CPU) // F_CPU is used to generate the receive send timings in some CPU's +#define CLOCKS_PER_MICRO (F_CPU / MICROS_IN_ONE_SECOND) +#endif + +/* + * For backwards compatibility + */ +#if defined(SYSCLOCK) // allow for processor specific code to define F_CPU +#undef F_CPU +#define F_CPU SYSCLOCK // Clock frequency to be used for timing. +#endif + +//#define DEBUG // Activate this for lots of lovely debug output from the IRremote core and all protocol decoders. +//#define TRACE // Activate this for more debug output. + +/** + * For better readability of code + */ +#define DISABLE_LED_FEEDBACK false +#define ENABLE_LED_FEEDBACK true +#define USE_DEFAULT_FEEDBACK_LED_PIN 0 + +/** + * The RAW_BUFFER_LENGTH determines the length of the byte buffer where the received IR timing data is stored before decoding. + * 100 is sufficient for standard protocols up to 48 bits, with 1 bit consisting of one mark and space plus 1 byte for initial gap, 2 bytes for header and 1 byte for stop bit. + * 48 bit protocols are PANASONIC, KASEIKYO, SAMSUNG48, RC6. + * 32 bit protocols like NEC, SAMSUNG, WHYNTER, SONY(20), LG(28) requires a buffer length of 68. + * 16 bit protocols like BOSEWAVE, DENON, FAST, JVC, LEGO_PF, RC5, SONY(12 or 15) requires a buffer length of 36. + * MAGIQUEST requires a buffer length of 112. + * Air conditioners often send a longer protocol data stream up to 750 bits. + */ +#if !defined(RAW_BUFFER_LENGTH) +# if (defined(RAMEND) && RAMEND <= 0x2FF) || (defined(RAMSIZE) && RAMSIZE < 0x2FF) +// for RAMsize <= 512 bytes +#define RAW_BUFFER_LENGTH 100 ///< Length of raw duration buffer. Must be even. 100 supports up to 48 bit codings inclusive 1 start and 1 stop bit. +# elif (defined(RAMEND) && RAMEND <= 0x8FF) || (defined(RAMSIZE) && RAMSIZE < 0x8FF) +// for RAMsize <= 2k +#define RAW_BUFFER_LENGTH 200 ///< Length of raw duration buffer. Must be even. 100 supports up to 48 bit codings inclusive 1 start and 1 stop bit. +# else +// For undefined or bigger RAMsize +#define RAW_BUFFER_LENGTH 750 // The value for air condition remotes. +# endif +#endif +#if RAW_BUFFER_LENGTH % 2 == 1 +#error RAW_BUFFER_LENGTH must be even, since the array consists of space / mark pairs. +#endif + +#if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program memory and speeds up ISR +typedef uint_fast8_t IRRawlenType; +#else +typedef unsigned int IRRawlenType; +#endif + +/* + * Use 8 bit buffer for IR timing in 50 ticks units. + * It is save to use 8 bit if RECORD_GAP_TICKS < 256, since any value greater 255 is interpreted as frame gap of 12750 us. + * The default for frame gap is currently 8000! + * But if we assume that for most protocols the frame gap is way greater than the biggest mark or space duration, + * we can choose to use a 8 bit buffer even for frame gaps up to 200000 us. + * This enables the use of 8 bit buffer even for more some protocols like B&O or LG air conditioner etc. + */ +#if RECORD_GAP_TICKS <= 400 // Corresponds to RECORD_GAP_MICROS of 200000. A value of 255 is foolproof, but we assume, that the frame gap is way greater than the biggest mark or space duration. +typedef uint8_t IRRawbufType; // all timings up to the gap fit into 8 bit. +#else +typedef uint16_t IRRawbufType; // The gap does not fit into 8 bit ticks value. This must not be a reason to use 16 bit for buffer, but it is at least save. +#endif + +#if (__INT_WIDTH__ < 32) +typedef uint32_t IRRawDataType; +#define BITS_IN_RAW_DATA_TYPE 32 +#else +typedef uint64_t IRRawDataType; +#define BITS_IN_RAW_DATA_TYPE 64 +#endif + +/********************************************************** + * Declarations for the receiver Interrupt Service Routine + **********************************************************/ +// ISR State-Machine : Receiver States +#define IR_REC_STATE_IDLE 0 // Counting the gap time and waiting for the start bit to arrive +#define IR_REC_STATE_MARK 1 // A mark was received and we are counting the duration of it. +#define IR_REC_STATE_SPACE 2 // A space was received and we are counting the duration of it. If space is too long, we assume end of frame. +#define IR_REC_STATE_STOP 3 // Stopped until set to IR_REC_STATE_IDLE which can only be done by resume() + +/** + * This struct contains the data and control used for receiver static functions and the ISR (interrupt service routine) + * Only StateForISR needs to be volatile. All the other fields are not written by ISR after data available and before start/resume. + */ +struct irparams_struct { + // The fields are ordered to reduce memory overflow caused by struct-padding + volatile uint8_t StateForISR; ///< State Machine state + uint_fast8_t IRReceivePin; ///< Pin connected to IR data from detector +#if defined(__AVR__) + volatile uint8_t *IRReceivePinPortInputRegister; + uint8_t IRReceivePinMask; +#endif + volatile uint_fast16_t TickCounterForISR; ///< Counts 50uS ticks. The value is copied into the rawbuf array on every transition. Counting is independent of state or resume(). +#if !defined(IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK) + void (*ReceiveCompleteCallbackFunction)(void); ///< The function to call if a protocol message has arrived, i.e. StateForISR changed to IR_REC_STATE_STOP +#endif + bool OverflowFlag; ///< Raw buffer OverflowFlag occurred + IRRawlenType rawlen; ///< counter of entries in rawbuf + uint16_t initialGapTicks; ///< Tick counts of the length of the gap between previous and current IR frame. Pre 4.4: rawbuf[0]. + IRRawbufType rawbuf[RAW_BUFFER_LENGTH]; ///< raw data / tick counts per mark/space. With 8 bit we can only store up to 12.7 ms. First entry is empty to be backwards compatible. +}; + +extern unsigned long sMicrosAtLastStopTimer; // Used to adjust TickCounterForISR with uncounted ticks between stopTimer() and restartTimer() + +#include "IRProtocol.h" + +/* + * Debug directives + * Outputs with IR_DEBUG_PRINT can only be activated by defining DEBUG! + * If LOCAL_DEBUG is defined in one file, all outputs with IR_DEBUG_PRINT are still suppressed. + */ +#if defined(DEBUG) || defined(TRACE) +# define IR_DEBUG_PRINT(...) Serial.print(__VA_ARGS__) +# define IR_DEBUG_PRINTLN(...) Serial.println(__VA_ARGS__) +#else +/** + * If DEBUG, print the arguments, otherwise do nothing. + */ +# define IR_DEBUG_PRINT(...) void() +/** + * If DEBUG, print the arguments as a line, otherwise do nothing. + */ +# define IR_DEBUG_PRINTLN(...) void() +#endif + +#if defined(TRACE) +# define IR_TRACE_PRINT(...) Serial.print(__VA_ARGS__) +# define IR_TRACE_PRINTLN(...) Serial.println(__VA_ARGS__) +#else +# define IR_TRACE_PRINT(...) void() +# define IR_TRACE_PRINTLN(...) void() +#endif + +/**************************************************** + * RECEIVING + ****************************************************/ + +/** + * Results returned from old decoders !!!deprecated!!! + */ +struct decode_results { + decode_type_t decode_type; // deprecated, moved to decodedIRData.protocol ///< UNKNOWN, NEC, SONY, RC5, ... + uint16_t address; // Used by Panasonic & Sharp [16-bits] + uint32_t value; // deprecated, moved to decodedIRData.decodedRawData ///< Decoded value / command [max 32-bits] + uint8_t bits; // deprecated, moved to decodedIRData.numberOfBits ///< Number of bits in decoded value + uint16_t magnitude; // deprecated, moved to decodedIRData.extra ///< Used by MagiQuest [16-bits] + bool isRepeat; // deprecated, moved to decodedIRData.flags ///< True if repeat of value is detected + +// next 3 values are copies of irparams_struct values - see above + uint16_t *rawbuf; // deprecated, moved to decodedIRData.rawDataPtr->rawbuf ///< Raw intervals in 50uS ticks + uint_fast8_t rawlen; // deprecated, moved to decodedIRData.rawDataPtr->rawlen ///< Number of records in rawbuf + bool overflow; // deprecated, moved to decodedIRData.flags ///< true if IR raw code too long +}; + +/** + * Main class for receiving IR signals + */ +class IRrecv { +public: + + IRrecv(); + IRrecv( + uint_fast8_t aReceivePin) + __attribute__ ((deprecated ("Please use the default IRrecv instance \"IrReceiver\" and IrReceiver.begin(), and not your own IRrecv instance."))); + IRrecv(uint_fast8_t aReceivePin, + uint_fast8_t aFeedbackLEDPin) + __attribute__ ((deprecated ("Please use the default IRrecv instance \"IrReceiver\" and IrReceiver.begin(), and not your own IRrecv instance.."))); + void setReceivePin(uint_fast8_t aReceivePinNumber); +#if !defined(IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK) + void registerReceiveCompleteCallback(void (*aReceiveCompleteCallbackFunction)(void)); +#endif + + /* + * Stream like API + */ + void begin(uint_fast8_t aReceivePin, bool aEnableLEDFeedback = false, uint_fast8_t aFeedbackLEDPin = + USE_DEFAULT_FEEDBACK_LED_PIN); + void start(); + void enableIRIn(); // alias for start + void restartTimer(); + void restartTimer(uint32_t aMicrosecondsToAddToGapCounter); + void restartTimerWithTicksToAdd(uint16_t aTicksToAddToGapCounter); + void restartAfterSend(); + + bool available(); + IRData* read(); // returns decoded data + // write is a method of class IRsend below + // size_t write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats = NO_REPEATS); + void stopTimer(); + void stop(); + void disableIRIn(); // alias for stop + void end(); // alias for stop + + bool isIdle(); + + /* + * The main functions + */ + bool decode(); // Check if available and try to decode + void resume(); // Enable receiving of the next value + + /* + * Useful info and print functions + */ + void printIRResultMinimal(Print *aSerial); + void printIRDuration(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks); + void printIRResultRawFormatted(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks = true); + void printIRResultAsCVariables(Print *aSerial); + uint8_t getMaximumMarkTicksFromRawData(); + uint8_t getMaximumSpaceTicksFromRawData(); + uint8_t getMaximumTicksFromRawData(bool aSearchSpaceInsteadOfMark); + uint32_t getTotalDurationOfRawData(); + + /* + * Next 4 functions are also available as non member functions + */ + bool printIRResultShort(Print *aSerial, bool aCheckForRecordGapsMicros = true); + void printDistanceWidthTimingInfo(Print *aSerial, DistanceWidthTimingInfoStruct *aDistanceWidthTimingInfo); + void printIRSendUsage(Print *aSerial); +#if defined(__AVR__) + const __FlashStringHelper* getProtocolString(); +#else + const char* getProtocolString(); +#endif + static void printActiveIRProtocols(Print *aSerial); + + void compensateAndPrintIRResultAsCArray(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks = true); + void compensateAndPrintIRResultAsPronto(Print *aSerial, uint16_t frequency = 38000U); + + /* + * Store the data for further processing + */ + void compensateAndStoreIRResultInArray(uint8_t *aArrayPtr); + size_t compensateAndStorePronto(String *aString, uint16_t frequency = 38000U); + + /* + * The main decoding functions used by the individual decoders + */ + bool decodePulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, uint_fast8_t aNumberOfBits, + IRRawlenType aStartOffset = 3); + + bool decodePulseDistanceWidthData_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, + uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset = 3); + + bool decodePulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros, + uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, bool aMSBfirst); + + bool decodePulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros, + uint16_t aZeroMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroSpaceMicros, bool aMSBfirst) + __attribute__ ((deprecated ("Please use decodePulseDistanceWidthData() with 6 parameters."))); + + bool decodePulseDistanceWidthDataStrict(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros, + uint16_t aZeroMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroSpaceMicros, bool aMSBfirst); + + bool decodeBiPhaseData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint_fast8_t aStartClockCount, + uint_fast8_t aValueOfSpaceToMarkTransition, uint16_t aBiphaseTimeUnit); + + void initBiphaselevel(uint_fast8_t aRCDecodeRawbuffOffset, uint16_t aBiphaseTimeUnit); + uint_fast8_t getBiphaselevel(); + + /* + * All standard (decode address + command) protocol decoders + */ + bool decodeBangOlufsen(); + bool decodeBoseWave(); + bool decodeDenon(); + bool decodeFAST(); + bool decodeJVC(); + bool decodeKaseikyo(); + bool decodeLegoPowerFunctions(); + bool decodeLG(); + bool decodeMagiQuest(); // not completely standard + bool decodeNEC(); + bool decodeRC5(); + bool decodeRC6(); + bool decodeSamsung(); + bool decodeSharp(); // redirected to decodeDenon() + bool decodeSony(); + bool decodeWhynter(); + + bool decodeDistanceWidth(); + + bool decodeHash(); + + // Template function :-) + bool decodeShuzu(); + + /* + * Old functions + */ + bool decodeDenonOld(decode_results *aResults); + bool decodeJVCMSB(decode_results *aResults); + bool decodeLGMSB(decode_results *aResults); + bool decodeNECMSB(decode_results *aResults); + bool decodePanasonicMSB(decode_results *aResults); + bool decodeSonyMSB(decode_results *aResults); + bool decodeSAMSUNG(decode_results *aResults); + bool decodeHashOld(decode_results *aResults); + + bool decode_old(decode_results *aResults); + + bool decode( + decode_results *aResults) + __attribute__ ((deprecated ("Please use IrReceiver.decode() without a parameter and IrReceiver.decodedIRData. ."))); + + // for backward compatibility. Now in IRFeedbackLED.hpp + void blink13(uint8_t aEnableLEDFeedback) + __attribute__ ((deprecated ("Please use setLEDFeedback() or enableLEDFeedback() / disableLEDFeedback()."))); + + /* + * Internal functions + */ + void initDecodedIRData(); + uint_fast8_t compare(uint16_t oldval, uint16_t newval); + bool checkHeader(PulseDistanceWidthProtocolConstants *aProtocolConstants); + bool checkHeader_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM); + void checkForRepeatSpaceTicksAndSetFlag(uint16_t aMaximumRepeatSpaceTicks); + bool checkForRecordGapsMicros(Print *aSerial); + + IRData decodedIRData; // Decoded IR data for the application + + // Last decoded IR data for repeat detection and to fill in JVC, LG, NEC repeat values. Parity for Denon autorepeat + decode_type_t lastDecodedProtocol; + uint16_t lastDecodedAddress; + uint16_t lastDecodedCommand; +#if defined(DECODE_DISTANCE_WIDTH) + IRRawDataType lastDecodedRawData; +#endif + + uint8_t repeatCount; // Used e.g. for Denon decode for autorepeat decoding. +}; + +extern uint_fast8_t sBiphaseDecodeRawbuffOffset; // + +/* + * Mark & Space matching functions + */ +bool matchTicks(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros); +bool matchMark(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros); +bool matchSpace(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros); + +/* + * Old function names + */ +bool MATCH(uint16_t measured, uint16_t desired); +bool MATCH_MARK(uint16_t measured_ticks, uint16_t desired_us); +bool MATCH_SPACE(uint16_t measured_ticks, uint16_t desired_us); + +int getMarkExcessMicros(); + +void printActiveIRProtocols(Print *aSerial); + +/**************************************************** + * Feedback LED related functions + ****************************************************/ +#define DO_NOT_ENABLE_LED_FEEDBACK 0x00 +#define LED_FEEDBACK_DISABLED_COMPLETELY 0x00 +#define LED_FEEDBACK_ENABLED_FOR_RECEIVE 0x01 +#define LED_FEEDBACK_ENABLED_FOR_SEND 0x02 +void setFeedbackLED(bool aSwitchLedOn); +void setLEDFeedback(uint8_t aFeedbackLEDPin, uint8_t aEnableLEDFeedback); // if aFeedbackLEDPin == 0, then take board BLINKLED_ON() and BLINKLED_OFF() functions +void setLEDFeedback(bool aEnableLEDFeedback); // Direct replacement for blink13() +void enableLEDFeedback(); +constexpr auto enableLEDFeedbackForReceive = enableLEDFeedback; // alias for enableLEDFeedback +void disableLEDFeedback(); +constexpr auto disableLEDFeedbackForReceive = disableLEDFeedback; // alias for enableLEDFeedback +void enableLEDFeedbackForSend(); +void disableLEDFeedbackForSend(); + +void setBlinkPin(uint8_t aFeedbackLEDPin) __attribute__ ((deprecated ("Please use setLEDFeedback()."))); // deprecated + +/* + * Pulse parms are ((X*50)-MARK_EXCESS_MICROS) for the Mark and ((X*50)+MARK_EXCESS_MICROS) for the Space. + * First MARK is the one after the long gap + * Pulse parameters in microseconds + */ +#if !defined(TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT) +#define TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT 25 // Relative tolerance (in percent) for matchTicks(), matchMark() and matchSpace() functions used for protocol decoding. +#endif + +#define TICKS(us) ((us)/MICROS_PER_TICK) // (us)/50 +#if MICROS_PER_TICK == 50 && TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT == 25 // Defaults +#define TICKS_LOW(us) ((us)/67 ) // 67 = MICROS_PER_TICK / ((100-25)/100) = (MICROS_PER_TICK * 100) / (100-25) +#define TICKS_HIGH(us) (((us)/40) + 1) // 40 = MICROS_PER_TICK / ((100+25)/100) = (MICROS_PER_TICK * 100) / (100+25) +#else +/** Lower tolerance for comparison of measured data */ +//#define LTOL (1.0 - (TOLERANCE/100.)) +#define LTOL (100 - TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT) +/** Upper tolerance for comparison of measured data */ +//#define UTOL (1.0 + (TOLERANCE/100.)) +#define UTOL (100 + TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT) +#define TICKS_LOW(us) ((uint16_t ) ((long) (us) * LTOL / (MICROS_PER_TICK * 100) )) +#define TICKS_HIGH(us) ((uint16_t ) ((long) (us) * UTOL / (MICROS_PER_TICK * 100) + 1)) +#endif + +/* + * The receiver instance + */ +extern IRrecv IrReceiver; + +/* + * The receiver interrupt handler for timer interrupt + */ +void IRReceiveTimerInterruptHandler(); + +/**************************************************** + * SENDING + ****************************************************/ + +/** + * Just for better readability of code + */ +#define NO_REPEATS 0 +#define SEND_REPEAT_COMMAND true ///< used for e.g. NEC, where a repeat is different from just repeating the data. + +/** + * Main class for sending IR signals + */ +class IRsend { +public: + IRsend(); + + /* + * IR_SEND_PIN is defined or fixed by timer, value of IR_SEND_PIN is then "DeterminedByTimer" + */ +#if defined(IR_SEND_PIN) + void begin(); + // The default parameter allowed to specify IrSender.begin(7); without errors, if IR_SEND_PIN was defined. But the semantics is not the one the user expect. + void begin(bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin); // 4.3.1 Removed default value USE_DEFAULT_FEEDBACK_LED_PIN for last parameter + // The next function is a dummy to avoid acceptance of pre 4.3 calls to begin(DISABLE_LED_FEEDBACK); + void begin(uint8_t aSendPin) +# if !defined (DOXYGEN) + __attribute__ ((deprecated ("ERROR: IR_SEND_PIN is still defined, therefore the function begin(aSendPin) is NOT available. You must disable '#define IR_SEND_PIN' to enable this function."))); +# endif + + // The next function is a dummy to avoid acceptance of pre 4.0 calls to begin(IR_SEND_PIN, DISABLE_LED_FEEDBACK); + void begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback) +# if !defined (DOXYGEN) + __attribute__ ((deprecated ("You must use begin() and enableLEDFeedback() or disableLEDFeedback() since version 4.3."))); +# endif +#else + IRsend(uint_fast8_t aSendPin); + void begin(uint_fast8_t aSendPin); + void setSendPin(uint_fast8_t aSendPin); // required if we use IRsend() as constructor + // Since 4.0 guarded and without default parameter + void begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin); // aFeedbackLEDPin can be USE_DEFAULT_FEEDBACK_LED_PIN +#endif + + size_t write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats = NO_REPEATS); + size_t write(decode_type_t aProtocol, uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats = NO_REPEATS); + + void enableIROut(uint_fast8_t aFrequencyKHz); +#if defined(SEND_PWM_BY_TIMER) + void enableHighFrequencyIROut(uint_fast16_t aFrequencyKHz); // Used for Bang&Olufsen +#endif + + /* + * Array functions + */ + void sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros, + uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, + IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, + int_fast8_t aNumberOfRepeats); + void sendPulseDistanceWidthFromPGMArray(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros, + uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, + IRRawDataType const *aDecodedRawDataPGMArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, + int_fast8_t aNumberOfRepeats); + void sendPulseDistanceWidthFromArray(PulseDistanceWidthProtocolConstants *aProtocolConstants, + IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, int_fast8_t aNumberOfRepeats); + void sendPulseDistanceWidthFromPGMArray(PulseDistanceWidthProtocolConstants *aProtocolConstants, + IRRawDataType const *aDecodedRawDataPGMArray, uint16_t aNumberOfBits, int_fast8_t aNumberOfRepeats); + void sendPulseDistanceWidthFromArray_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, + IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, int_fast8_t aNumberOfRepeats); + void sendPulseDistanceWidthFromPGMArray_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, + IRRawDataType const *aDecodedRawDataPGMArray, uint16_t aNumberOfBits, int_fast8_t aNumberOfRepeats); + + void sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, DistanceWidthTimingInfoStruct *aDistanceWidthTimingInfo, + IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, + int_fast8_t aNumberOfRepeats); + void sendPulseDistanceWidthFromArray_P(uint_fast8_t aFrequencyKHz, + DistanceWidthTimingInfoStruct const *aDistanceWidthTimingInfoPGM, IRRawDataType *aDecodedRawDataArray, + uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, int_fast8_t aNumberOfRepeats); + + void sendPulseDistanceWidth(PulseDistanceWidthProtocolConstants *aProtocolConstants, IRRawDataType aData, + uint_fast8_t aNumberOfBits, int_fast8_t aNumberOfRepeats); + void sendPulseDistanceWidth_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, IRRawDataType aData, + uint_fast8_t aNumberOfBits, int_fast8_t aNumberOfRepeats); + void sendPulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, IRRawDataType aData, + uint_fast8_t aNumberOfBits); + void sendPulseDistanceWidthData_P(PulseDistanceWidthProtocolConstants const *aProtocolConstantsPGM, IRRawDataType aData, + uint_fast8_t aNumberOfBits); + void sendPulseDistanceWidth(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros, + uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, + IRRawDataType aData, uint_fast8_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, + int_fast8_t aNumberOfRepeats, void (*aSpecialSendRepeatFunction)() = nullptr); + void sendPulseDistanceWidth(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros, + uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, + IRRawDataType aData, uint_fast8_t aNumberOfBits, bool aMSBFirst, bool aSendStopBit, uint16_t aRepeatPeriodMillis, + int_fast8_t aNumberOfRepeats, void (*aSpecialSendRepeatFunction)() = nullptr) + __attribute__ ((deprecated ("Since version 4.1.0 parameter aSendStopBit is not longer required."))); + void sendPulseDistanceWidthData(uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, + uint16_t aZeroSpaceMicros, IRRawDataType aData, uint_fast8_t aNumberOfBits, uint8_t aFlags); + void sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits); + + void mark(uint16_t aMarkMicros); + static void space(uint16_t aSpaceMicros); + void IRLedOff(); + +// 8 Bit array + void sendRaw(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz); + void sendRaw_P(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz); + +// 16 Bit array + void sendRaw(const uint16_t aBufferWithMicroseconds[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz); + void sendRaw_P(const uint16_t aBufferWithMicroseconds[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz); + + /* + * New send functions + */ + void sendBangOlufsen(uint16_t aHeader, uint8_t aData, int_fast8_t aNumberOfRepeats = NO_REPEATS, + int8_t aNumberOfHeaderBits = 8); + void sendBangOlufsenDataLink(uint32_t aHeader, uint8_t aData, int_fast8_t aNumberOfRepeats = NO_REPEATS, + int8_t aNumberOfHeaderBits = 8); + void sendBangOlufsenRaw(uint32_t aRawData, int_fast8_t aBits, bool aBackToBack = false); + void sendBangOlufsenRawDataLink(uint64_t aRawData, int_fast8_t aBits, bool aBackToBack = false, + bool aUseDatalinkTiming = false); + void sendBoseWave(uint8_t aCommand, int_fast8_t aNumberOfRepeats = NO_REPEATS); + void sendDenon(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint8_t aSendSharpFrameMarker = 0); + void sendDenonRaw(uint16_t aRawData, int_fast8_t aNumberOfRepeats = NO_REPEATS) +#if !defined (DOXYGEN) + __attribute__ ((deprecated ("Please use sendDenon(aAddress, aCommand, aNumberOfRepeats)."))); +#endif + void sendFAST(uint8_t aCommand, int_fast8_t aNumberOfRepeats); + void sendJVC(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats); + + void sendLG2Repeat(); + uint32_t computeLGRawDataAndChecksum(uint8_t aAddress, uint16_t aCommand); + void sendLG(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats); + void sendLG2(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats); + void sendLGRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats = NO_REPEATS); + + void sendNECRepeat(); + uint32_t computeNECRawDataAndChecksum(uint16_t aAddress, uint16_t aCommand); + void sendNEC(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats); + void sendNEC2(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats); + void sendNECRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats = NO_REPEATS); + // NEC variants + void sendOnkyo(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats); + void sendApple(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats); + + void sendKaseikyo(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats, uint16_t aVendorCode); // LSB first + void sendPanasonic(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first + void sendKaseikyo_Denon(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first + void sendKaseikyo_Mitsubishi(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first + void sendKaseikyo_Sharp(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first + void sendKaseikyo_JVC(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first + + void sendRC5(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle = true); + void sendRC6(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle = true); + void sendRC6A(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint16_t aCustomer, + bool aEnableAutomaticToggle = true); + void sendSamsungLGRepeat(); + void sendSamsung(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats); + void sendSamsung16BitAddressAnd8BitCommand(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats); + void sendSamsung16BitAddressAndCommand(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats); + void sendSamsung48(uint16_t aAddress, uint32_t aCommand, int_fast8_t aNumberOfRepeats); + void sendSamsungLG(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats); + void sendSharp(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats); // redirected to sendDenon + void sendSharp2(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats); // redirected to sendDenon + void sendSony(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint8_t numberOfBits = 12); // SIRCS_12_PROTOCOL + + void sendLegoPowerFunctions(uint8_t aChannel, uint8_t tCommand, uint8_t aMode, bool aDoSend5Times = true); + void sendLegoPowerFunctions(uint16_t aRawData, bool aDoSend5Times = true); + void sendLegoPowerFunctions(uint16_t aRawData, uint8_t aChannel, bool aDoSend5Times = true); + + void sendMagiQuest(uint32_t aWandId, uint16_t aMagnitude); + + void sendPronto(const __FlashStringHelper *str, int_fast8_t aNumberOfRepeats = NO_REPEATS); + void sendPronto(const char *prontoHexString, int_fast8_t aNumberOfRepeats = NO_REPEATS); + void sendPronto(const uint16_t *data, uint16_t length, int_fast8_t aNumberOfRepeats = NO_REPEATS); + +#if defined(__AVR__) + void sendPronto_PF(uint_farptr_t str, int_fast8_t aNumberOfRepeats = NO_REPEATS); + void sendPronto_P(const char *str, int_fast8_t aNumberOfRepeats); +#endif + +// Template protocol :-) + void sendShuzu(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats); + + /* + * OLD send functions + */ + void sendDenon(unsigned long data, + int nbits) + __attribute__ ((deprecated ("The function sendDenon(data, nbits) is deprecated and may not work as expected! Use sendDenonRaw(data, NumberOfRepeats) or better sendDenon(Address, Command, NumberOfRepeats)."))); + void sendDish(uint16_t aData); + void sendJVC(unsigned long data, int nbits, + bool repeat) + __attribute__ ((deprecated ("This old function sends MSB first! Please use sendJVC(aAddress, aCommand, aNumberOfRepeats)."))) { + sendJVCMSB(data, nbits, repeat); + } + void sendJVCMSB(unsigned long data, int nbits, bool repeat = false); + + void sendLG(unsigned long data, + int nbits) + __attribute__ ((deprecated ("The function sendLG(data, nbits) is deprecated and may not work as expected! Use sendLGRaw(data, NumberOfRepeats) or better sendLG(Address, Command, NumberOfRepeats)."))); + + void sendNEC(uint32_t aRawData, + uint8_t nbits) + __attribute__ ((deprecated ("This old function sends MSB first! Please use sendNECMSB() or sendNEC(aAddress, aCommand, aNumberOfRepeats)."))) { + sendNECMSB(aRawData, nbits); + } + void sendNECMSB(uint32_t data, uint8_t nbits, bool repeat = false); + void sendRC5(uint32_t data, uint8_t nbits); + void sendRC5ext(uint8_t addr, uint8_t cmd, bool toggle); + void sendRC6Raw(uint32_t data, uint8_t nbits); + void sendRC6(uint32_t data, uint8_t nbits) __attribute__ ((deprecated ("Please use sendRC6Raw()."))); + void sendRC6Raw(uint64_t data, uint8_t nbits); + void sendRC6(uint64_t data, uint8_t nbits) __attribute__ ((deprecated ("Please use sendRC6Raw()."))); + ; + void sendSharpRaw(unsigned long data, int nbits); + void sendSharp(uint16_t address, uint16_t command); + void sendSAMSUNG(unsigned long data, int nbits); + __attribute__ ((deprecated ("This old function sends MSB first! Please use sendSamsung()."))); + void sendSamsungMSB(unsigned long data, int nbits); + void sendSonyMSB(unsigned long data, int nbits); + void sendSony(unsigned long data, + int nbits) + __attribute__ ((deprecated ("This old function sends MSB first! Please use sendSony(aAddress, aCommand, aNumberOfRepeats)."))); + ; + void sendWhynter(uint32_t aData, uint8_t aNumberOfBitsToSend); + +#if !defined(IR_SEND_PIN) + uint8_t sendPin; +#endif + uint16_t periodTimeMicros; + uint16_t periodOnTimeMicros; // compensated with PULSE_CORRECTION_NANOS for duration of digitalWrite. Around 8 microseconds for 38 kHz. + uint16_t getPulseCorrectionNanos(); + + static void customDelayMicroseconds(unsigned long aMicroseconds); +}; + +/* + * The sender instance + */ +extern IRsend IrSender; + +void sendNECSpecialRepeat(); +void sendLG2SpecialRepeat(); +void sendSamsungLGSpecialRepeat(); + +#endif // _IR_REMOTE_INT_H diff --git a/src/LongUnion.h b/src/LongUnion.h new file mode 100644 index 000000000..8f90f6135 --- /dev/null +++ b/src/LongUnion.h @@ -0,0 +1,153 @@ +/* + * LongUnion.h + * + * Copyright (C) 2020-2022 Armin Joachimsmeyer + * Email: armin.joachimsmeyer@gmail.com + * + * This file is part of Arduino-Utils https://github.com/ArminJo/Arduino-Utils. + * + * Arduino-Utils is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#if !defined(_WORD_UNION_H) || !defined(_LONG_UNION_H) || !defined(_LONG_LONG_UNION_H) + +#include + +#ifndef _WORD_UNION_H +#define _WORD_UNION_H +/** + * Union to specify parts / manifestations of a 16 bit Word without casts and shifts. + * It also supports the compiler generating small code. + * Usage: WordUnion tWord; + * tWord.UByte.HighByte = 0x12; + */ +union WordUnion { + struct { + uint8_t LowByte; + uint8_t HighByte; + } UByte; + struct { + int8_t LowByte; + int8_t HighByte; + } Byte; + uint8_t UBytes[2]; // UBytes[0] is LowByte + int8_t Bytes[2]; + uint16_t UWord; + int16_t Word; + uint8_t *BytePointer; +}; +#endif // _WORD_UNION_H + +#ifndef _LONG_UNION_H +#define _LONG_UNION_H +/** + * Union to specify parts / manifestations of a 32 bit Long without casts and shifts. + * It also supports the compiler generating small code. + */ +union LongUnion { + struct { + uint8_t LowByte; + uint8_t MidLowByte; + uint8_t MidHighByte; + uint8_t HighByte; + } UByte; + struct { + int8_t LowByte; + int8_t MidLowByte; + int8_t MidHighByte; + int8_t HighByte; + } Byte; + /* Does not work for STM32 + struct { + uint8_t LowByte; + uint16_t MidWord; + uint8_t HighByte; + } UByteWord; + */ + struct { + uint16_t LowWord; + uint16_t HighWord; + } UWord; + struct { + int16_t LowWord; + int16_t HighWord; + } Word; + struct { + WordUnion LowWord; + WordUnion HighWord; + } TwoWordUnions; + uint8_t UBytes[4]; // seems to have the same code size as using struct UByte + int8_t Bytes[4]; // Bytes[0] is LowByte + uint16_t UWords[2]; + int16_t Words[2]; + uint32_t ULong; + int32_t Long; + float Float; +}; +#endif // _LONG_UNION_H + +#ifndef _LONG_LONG_UNION_H +#define _LONG_LONG_UNION_H +/** + * Union to specify parts / manifestations of a 64 bit LongLong without casts and shifts. + * It also supports the compiler generating small code. + */ +union LongLongUnion { + struct { + uint16_t LowWord; + uint16_t MidLowWord; + uint16_t MidHighWord; + uint16_t HighWord; + } UWord; + struct { + int16_t LowWord; + int16_t MidLowWord; + int16_t MidHighWord; + int16_t HighWord; + } Word; + struct { + WordUnion LowWord; + WordUnion MidLowWord; + WordUnion MidHighWord; + WordUnion HighWord; + } FourWordUnions; + struct { + uint32_t LowLong; + uint32_t HighLong; + } ULong; + struct { + int32_t LowLong; + int32_t HighLong; + } Long; + struct { + LongUnion LowLong; + LongUnion HighLong; + } TwoLongUnions; + uint8_t UBytes[8]; // seems to have the same code size as using struct UByte + int8_t Bytes[8]; + uint16_t UWords[4]; + int16_t Words[4]; + uint64_t ULongLong; + int64_t LongLong; +#if __DBL_MANT_DIG__== 24 + float Floats[2]; // 32 bit double, as for AVR +#else + // 64 bit double + double Double; +#endif +}; +#endif // _LONG_LONG_UNION_H + +#endif // !defined(_WORD_UNION_H) || !defined(_LONG_UNION_H) || !defined(_LONG_LONG_UNION_H) diff --git a/src/TinyIR.h b/src/TinyIR.h new file mode 100644 index 000000000..8e29871e3 --- /dev/null +++ b/src/TinyIR.h @@ -0,0 +1,300 @@ +/* + * TinyIR.h + * + * + * Copyright (C) 2021-2025 Armin Joachimsmeyer + * armin.joachimsmeyer@gmail.com + * + * This file is part of IRMP https://github.com/IRMP-org/IRMP. + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + * TinyIRReceiver is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef _TINY_IR_H +#define _TINY_IR_H + +#include + +#include "LongUnion.h" + +/** \addtogroup TinyReceiver Minimal receiver for NEC and FAST protocol + * @{ + */ + +#define VERSION_TINYIR "2.2.0" +#define VERSION_TINYIR_MAJOR 2 +#define VERSION_TINYIR_MINOR 2 +#define VERSION_TINYIR_PATCH 0 +// The change log is at the bottom of the file + +/** + * Timing for NEC protocol + * + * see: https://www.sbprojects.net/knowledge/ir/nec.php + * LSB first, 1 start bit + 16 bit address + 8 bit data + 8 bit inverted data + 1 stop bit. + */ +#if !defined(NEC_ADDRESS_BITS) +#define NEC_ADDRESS_BITS 16 // 16 bit address or 8 bit address and 8 bit inverted address +#define NEC_COMMAND_BITS 16 // Command and inverted command +#define NEC_BITS (NEC_ADDRESS_BITS + NEC_COMMAND_BITS) + +#define NEC_UNIT 560 + +#define NEC_HEADER_MARK (16 * NEC_UNIT) // 9000 +#define NEC_HEADER_SPACE (8 * NEC_UNIT) // 4500 + +#define NEC_BIT_MARK NEC_UNIT +#define NEC_ONE_SPACE (3 * NEC_UNIT) // 1690 +#define NEC_ZERO_SPACE NEC_UNIT + +#define NEC_REPEAT_HEADER_SPACE (4 * NEC_UNIT) // 2250 + +#define NEC_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down. +#define NEC_MINIMAL_DURATION 49900 // NEC_HEADER_MARK + NEC_HEADER_SPACE + 32 * 2 * NEC_UNIT + NEC_UNIT // 2.5 because we assume more zeros than ones +#define NEC_MAXIMUM_REPEAT_DISTANCE (NEC_REPEAT_PERIOD - NEC_MINIMAL_DURATION + 10000) // 70 ms +#endif + +/** + * The FAST protocol is a proprietary modified JVC protocol without address, with parity and with a shorter header. + * FAST protocol characteristics: + * - Bit timing is like NEC or JVC + * - The header is shorter, 3156 vs. 12500 + * - No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command, + * leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms. + * - Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance. + */ +/* + Protocol=FAST Address=0x0 Command=0x76 Raw-Data=0x8976 16 bits LSB first + +2100,-1050 + + 550,- 500 + 550,-1550 + 550,-1550 + 550,- 500 + + 550,-1550 + 550,-1550 + 550,-1550 + 550,- 500 + + 550,-1550 + 550,- 500 + 550,- 500 + 550,-1550 + + 550,- 500 + 550,- 500 + 550,- 500 + 550,-1550 + + 550 + Sum: 28900 + */ +#define FAST_KHZ 38 +#define FAST_ADDRESS_BITS 0 // No address +#define FAST_COMMAND_BITS 16 // Command and inverted command (parity) +#define FAST_BITS (FAST_ADDRESS_BITS + FAST_COMMAND_BITS) + +#define FAST_UNIT 526 // 20 periods of 38 kHz (526.315789) + +#define FAST_BIT_MARK FAST_UNIT +#define FAST_ONE_SPACE (3 * FAST_UNIT) // 1578 -> bit period = 2104 +#define FAST_ZERO_SPACE FAST_UNIT // 526 -> bit period = 1052 + +#define FAST_HEADER_MARK (4 * FAST_UNIT) // 2104 +#define FAST_HEADER_SPACE (2 * FAST_UNIT) // 1052 + +#define FAST_REPEAT_PERIOD 50000 // Commands are repeated every 50 ms (measured from start to start) for as long as the key on the remote control is held down. +#define FAST_REPEAT_DISTANCE (FAST_REPEAT_PERIOD - (55 * FAST_UNIT)) // 19 ms +#define FAST_MAXIMUM_REPEAT_DISTANCE (FAST_REPEAT_DISTANCE + 10000) // 29 ms + +/* + * Definitions to switch between FAST and NEC/ONKYO timing with the same code. + */ +#if defined(USE_FAST_PROTOCOL) +#define ENABLE_NEC2_REPEATS // Disables detection of special short frame NEC repeats. Saves 40 bytes program memory. + +#define TINY_RECEIVER_ADDRESS_BITS FAST_ADDRESS_BITS +#define TINY_RECEIVER_COMMAND_BITS FAST_COMMAND_BITS +#if !defined(TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY) +#define TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY true // 8 bit and 8 bit parity +//#define TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY false // 16 bit command without parity - not tested +#endif + +#define TINY_RECEIVER_BITS FAST_BITS +#define TINY_RECEIVER_UNIT FAST_UNIT + +#define TINY_RECEIVER_HEADER_MARK FAST_HEADER_MARK +#define TINY_RECEIVER_HEADER_SPACE FAST_HEADER_SPACE + +#define TINY_RECEIVER_BIT_MARK FAST_BIT_MARK +#define TINY_RECEIVER_ONE_SPACE FAST_ONE_SPACE +#define TINY_RECEIVER_ZERO_SPACE FAST_ZERO_SPACE + +#define TINY_RECEIVER_MAXIMUM_REPEAT_DISTANCE FAST_MAXIMUM_REPEAT_DISTANCE // for repeat detection + +#else + +#define TINY_RECEIVER_ADDRESS_BITS NEC_ADDRESS_BITS // the address bits + parity +# if defined(USE_ONKYO_PROTOCOL) +#define TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY false // 16 bit address without parity +# elif defined(USE_EXTENDED_NEC_PROTOCOL) +#define TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY false // 16 bit address without parity +# else +#define TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY true // 8 bit and 8 bit parity +# endif + +#define TINY_RECEIVER_COMMAND_BITS NEC_COMMAND_BITS // the command bits + parity +# if defined(USE_ONKYO_PROTOCOL) +#define TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY false // 16 bit command without parity +# else +#define TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY true // 8 bit and 8 bit parity +# endif + +#define TINY_RECEIVER_BITS NEC_BITS +#define TINY_RECEIVER_UNIT NEC_UNIT + +#define TINY_RECEIVER_HEADER_MARK NEC_HEADER_MARK +#define TINY_RECEIVER_HEADER_SPACE NEC_HEADER_SPACE + +#define TINY_RECEIVER_BIT_MARK NEC_BIT_MARK +#define TINY_RECEIVER_ONE_SPACE NEC_ONE_SPACE +#define TINY_RECEIVER_ZERO_SPACE NEC_ZERO_SPACE + +#define TINY_RECEIVER_MAXIMUM_REPEAT_DISTANCE NEC_MAXIMUM_REPEAT_DISTANCE +#endif + +#if defined(USE_CALLBACK_FOR_TINY_RECEIVER) +/* + * This function is called, if a complete command was received and must be implemented in the file (user code) + * which includes this library if USE_CALLBACK_FOR_TINY_RECEIVER is activated. + */ +extern void handleReceivedTinyIRData(); +#endif + +#if !defined(MICROS_IN_ONE_SECOND) +#define MICROS_IN_ONE_SECOND 1000000L +#endif + +#if !defined(MICROS_IN_ONE_MILLI) +#define MICROS_IN_ONE_MILLI 1000L +#endif + +/* + * Macros for comparing timing values + */ +#define lowerValue25Percent(aDuration) (aDuration - (aDuration / 4)) +#define upperValue25Percent(aDuration) (aDuration + (aDuration / 4)) +#define lowerValue50Percent(aDuration) (aDuration / 2) // (aDuration - (aDuration / 2)) +#define upperValue50Percent(aDuration) (aDuration + (aDuration / 2)) + +/* + * The states for the state machine + */ +#define IR_RECEIVER_STATE_WAITING_FOR_START_MARK 0 +#define IR_RECEIVER_STATE_WAITING_FOR_START_SPACE 1 +#define IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK 2 +#define IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE 3 +#define IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK 4 +#define IR_RECEIVER_STATE_WAITING_FOR_STOP_MARK 5 +/** + * Control and data variables of the state machine for TinyReceiver + */ +struct TinyIRReceiverStruct { + /* + * State machine + */ + uint32_t LastChangeMicros; ///< Microseconds of last Pin Change Interrupt. + uint8_t IRReceiverState; ///< The state of the state machine. + uint8_t IRRawDataBitCounter; ///< How many bits are currently contained in raw data. + /* + * Data + */ +#if (TINY_RECEIVER_BITS > 16) + uint32_t IRRawDataMask; ///< The corresponding bit mask for IRRawDataBitCounter. + LongUnion IRRawData; ///< The current raw data. LongUnion helps with decoding of address and command. +#else + uint16_t IRRawDataMask; ///< The corresponding bit mask for IRRawDataBitCounter. + WordUnion IRRawData; ///< The current raw data. WordUnion helps with decoding of command. +#endif + uint8_t Flags; ///< One of IRDATA_FLAGS_EMPTY, IRDATA_FLAGS_IS_REPEAT, and IRDATA_FLAGS_PARITY_FAILED +}; + +/* + * Definitions for member TinyIRReceiverCallbackDataStruct.Flags + * This is a copy of flags from IRremoteInt.h + */ +#define IRDATA_FLAGS_EMPTY 0x00 +#define IRDATA_FLAGS_IS_REPEAT 0x01 +#define IRDATA_FLAGS_IS_AUTO_REPEAT 0x02 // not used for TinyIR +#define IRDATA_FLAGS_PARITY_FAILED 0x04 ///< the current (autorepeat) frame violated parity check + +/** + * Is filled before calling the user callback to transfer received data to main loop for further processing. + */ +struct TinyIRReceiverCallbackDataStruct { +#if (TINY_RECEIVER_ADDRESS_BITS > 0) +# if (TINY_RECEIVER_ADDRESS_BITS == 16) && !TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY + uint16_t Address; +# else + uint8_t Address; +# endif +#endif + +# if (TINY_RECEIVER_COMMAND_BITS == 16) && !TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY + uint16_t Command; +#else + uint8_t Command; +#endif + uint8_t Flags; // Bit coded flags. Can contain one of the bits: IRDATA_FLAGS_IS_REPEAT and IRDATA_FLAGS_PARITY_FAILED + bool justWritten; ///< Is set true if new data is available. Used by the main loop / TinyReceiverDecode(), to avoid multiple evaluations of the same IR frame. +}; +extern volatile TinyIRReceiverCallbackDataStruct TinyIRReceiverData; + +bool isIRReceiverAttachedForTinyReceiver(); +bool initPCIInterruptForTinyReceiver(); +bool enablePCIInterruptForTinyReceiver(); +void disablePCIInterruptForTinyReceiver(); +bool isTinyReceiverIdle(); +bool TinyReceiverDecode(); +void printTinyReceiverResultMinimal(Print *aSerial); + +void sendFAST(uint8_t aSendPin, uint16_t aCommand, uint_fast8_t aNumberOfRepeats = 0); +void sendFast8BitAndParity(uint8_t aSendPin, uint8_t aCommand, uint_fast8_t aNumberOfRepeats = 0); +void sendONKYO(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats = 0, bool aSendNEC2Repeats = false); // Send NEC with 16 bit command, even if aCommand < 0x100 +void sendNECMinimal(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats = 0) + __attribute__ ((deprecated ("Renamed to sendNEC()."))); +void sendNEC(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats = 0, bool aSendNEC2Repeats = false); +void sendExtendedNEC(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats = 0, bool aSendNEC2Repeats = false); + +#if defined(NO_LED_FEEDBACK_CODE) +# if !defined(NO_LED_RECEIVE_FEEDBACK_CODE) +#define NO_LED_RECEIVE_FEEDBACK_CODE +# endif +# if !defined(NO_LED_SEND_FEEDBACK_CODE) +#define NO_LED_SEND_FEEDBACK_CODE +# endif +#endif + +#if !defined(IR_FEEDBACK_LED_PIN) && defined(LED_BUILTIN) +#define IR_FEEDBACK_LED_PIN LED_BUILTIN +#endif + +/* + * Version 2.2.0 - 7/2024 + * - New TinyReceiverDecode() function to be used as drop in for IrReceiver.decode(). + * + * Version 2.1.0 - 2/2024 + * - New sendExtendedNEC() function and new parameter aSendNEC2Repeats. + * + * Version 2.0.0 - 10/2023 + * - New TinyIRReceiverData which is filled with address, command and flags. + * - Removed parameters address, command and flags from callback handleReceivedTinyIRData() and printTinyReceiverResultMinimal(). + * - Callback function now only enabled if USE_CALLBACK_FOR_TINY_RECEIVER is activated. + * + * Version 1.2.0 - 01/2023 + * - Added ONKYO protocol, NEC with 16 bit address and command, instead of 8 bit + 8 bit parity address and command. + * - Renamed functions and macros. + * + * Version 1.1.0 - 01/2023 + * - FAST protocol added. + */ +/** @}*/ + +#endif // _TINY_IR_H diff --git a/src/TinyIRReceiver.hpp b/src/TinyIRReceiver.hpp new file mode 100644 index 000000000..af66d3c84 --- /dev/null +++ b/src/TinyIRReceiver.hpp @@ -0,0 +1,723 @@ +/* + * TinyIRReceiver.hpp + * + * Receives IR data of NEC protocol using pin change interrupts. + * NEC is the protocol of most cheap remote controls for Arduino. + * + * Parity check is done for address and data. + * On a completely received IR command, the user function handleReceivedIRData(uint8_t aAddress, uint8_t aCommand, uint8_t aFlags) + * is called in interrupt context but with interrupts being enabled to enable use of delay() etc. + * !!!!!!!!!!!!!!!!!!!!!! + * Functions called in interrupt context should be running as short as possible, + * so if you require longer action, save the data (address + command) and handle them in the main loop. + * !!!!!!!!!!!!!!!!!!!!! + * aFlags can contain one of IRDATA_FLAGS_EMPTY, IRDATA_FLAGS_IS_REPEAT and IRDATA_FLAGS_PARITY_FAILED bits + * + * The FAST protocol is a proprietary modified JVC protocol without address, with parity and with a shorter header. + * FAST Protocol characteristics: + * - Bit timing is like NEC or JVC + * - The header is shorter, 3156 vs. 12500 + * - No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command, + * leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms. + * - Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance. + * + * + * This file is part of IRMP https://github.com/IRMP-org/IRMP. + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2022-2025 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +/* + * This library can be configured at compile time by the following options / macros: + * For more details see: https://github.com/Arduino-IRremote/Arduino-IRremote#compile-options--macros-for-this-library (scroll down) + * + * - IR_RECEIVE_PIN The pin number for TinyIRReceiver IR input. + * - IR_FEEDBACK_LED_PIN The pin number for TinyIRReceiver feedback LED. + * - NO_LED_FEEDBACK_CODE Disables the feedback LED function for send and receive. Saves 14 bytes program memory. + * - NO_LED_RECEIVE_FEEDBACK_CODE Disables the LED feedback code for receive. + * - NO_LED_SEND_FEEDBACK_CODE Disables the LED feedback code for send. + * - DISABLE_PARITY_CHECKS Disable parity checks. Saves 48 bytes of program memory. + * - USE_EXTENDED_NEC_PROTOCOL Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value. + * - USE_ONKYO_PROTOCOL Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value. + * - USE_FAST_PROTOCOL Use FAST protocol (no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command) instead of NEC. + * - ENABLE_NEC2_REPEATS Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat. + * - USE_CALLBACK_FOR_TINY_RECEIVER Call the user provided function "void handleReceivedTinyIRData()" each time a frame or repeat is received. + */ + +#ifndef _TINY_IR_RECEIVER_HPP +#define _TINY_IR_RECEIVER_HPP + +#include + +/* + * Protocol selection + */ +//#define DISABLE_PARITY_CHECKS // Disable parity checks. Saves 48 bytes of program memory. +//#define USE_EXTENDED_NEC_PROTOCOL // Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value. +//#define USE_ONKYO_PROTOCOL // Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value. +//#define USE_FAST_PROTOCOL // Use FAST protocol instead of NEC / ONKYO. +//#define ENABLE_NEC2_REPEATS // Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat. + +//#define IR_RECEIVE_PIN 2 +//#define NO_LED_RECEIVE_FEEDBACK_CODE // Disables the LED feedback code for receive. +//#define IR_FEEDBACK_LED_PIN 12 // Use this, to disable use of LED_BUILTIN definition for IR_FEEDBACK_LED_PIN +#include "TinyIR.h" + +#include "digitalWriteFast.h" +/** \addtogroup TinyReceiver Minimal receiver for NEC and FAST protocol + * @{ + */ + +#if defined(DEBUG) +#define LOCAL_DEBUG +#define LOCAL_DEBUG_ATTACH_INTERRUPT +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +//#define LOCAL_DEBUG_ATTACH_INTERRUPT // To see if attachInterrupt() or static interrupt (by register tweaking) is used and no other debug output +#endif + +#if defined(TRACE) +#define LOCAL_TRACE_STATE_MACHINE +#else +//#define LOCAL_TRACE_STATE_MACHINE // to see the state of the ISR (Interrupt Service Routine) state machine +#endif + +//#define _IR_MEASURE_TIMING // Activate this if you want to enable internal hardware timing measurement. +//#define _IR_TIMING_TEST_PIN 7 +TinyIRReceiverStruct TinyIRReceiverControl; +volatile TinyIRReceiverCallbackDataStruct TinyIRReceiverData; + +/* + * Set input pin and output pin definitions etc. + */ +#if defined(IR_INPUT_PIN) +#warning "IR_INPUT_PIN is deprecated, use IR_RECEIVE_PIN" +#define IR_RECEIVE_PIN IR_INPUT_PIN +#endif + +#if !defined(IR_RECEIVE_PIN) +# if defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) +#warning "IR_RECEIVE_PIN is not defined, so it is set to 10" +#define IR_RECEIVE_PIN 10 +# elif defined(__AVR_ATtiny816__) +#warning "IR_RECEIVE_PIN is not defined, so it is set to 14" +#define IR_RECEIVE_PIN 14 +# else +#warning "IR_RECEIVE_PIN is not defined, so it is set to 2" +#define IR_RECEIVE_PIN 2 +# endif +#endif + + +#if !( \ + (defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)) /* ATtinyX5 */ \ +|| defined(__AVR_ATtiny88__) /* MH-ET LIVE Tiny88 */ \ +|| defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) \ +|| defined(__AVR_ATmega8__) || defined(__AVR_ATmega48__) || defined(__AVR_ATmega48P__) || defined(__AVR_ATmega48PB__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega88PB__) \ +|| defined(__AVR_ATmega168__) || defined(__AVR_ATmega168PA__) || defined(__AVR_ATmega168PB__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) \ + /* ATmegas with ports 0,1,2 above and ATtiny167 only 2 pins below */ \ +|| ( (defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)) && ( (defined(ARDUINO_AVR_DIGISPARKPRO) && ((IR_RECEIVE_PIN == 3) || (IR_RECEIVE_PIN == 9))) /*ATtinyX7(digisparkpro) and pin 3 or 9 */\ + || (! defined(ARDUINO_AVR_DIGISPARKPRO) && ((IR_RECEIVE_PIN == 3) || (IR_RECEIVE_PIN == 14)))) ) /*ATtinyX7(ATTinyCore) and pin 3 or 14 */ \ +) +#define TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT // Cannot use any static ISR vector here. In other cases we have code provided for generating interrupt on pin change. +#endif + +/** + * Declaration of the callback function provided by the user application. + * It is called every time a complete IR command or repeat was received. + */ +extern void handleReceivedTinyIRData(); + +uint32_t sMicrosOfGap; // The length of the gap before the start bit, used for trace +/** + * The ISR (Interrupt Service Routine) of TinyIRRreceiver. + * It handles the NEC protocol decoding and calls the user callback function on complete. + * 5 us + 3 us for push + pop for a 16MHz ATmega + */ +#if defined(ESP8266) || defined(ESP32) +IRAM_ATTR +#endif +void IRPinChangeInterruptHandler(void) { +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) + digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles +#endif + /* + * Save IR input level + * Negative logic, true / HIGH means inactive / IR space, LOW / false means IR mark. + */ + uint_fast8_t tIRLevel = digitalReadFast(IR_RECEIVE_PIN); + +#if !defined(NO_LED_RECEIVE_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN) +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(IR_FEEDBACK_LED_PIN, tIRLevel); +# else + digitalWriteFast(IR_FEEDBACK_LED_PIN, !tIRLevel); +# endif +#endif + + /* + * 1. compute microseconds after last change + */ + // Repeats can be sent after a pause, which is longer than 64000 microseconds, so we need a 32 bit value for check of repeats + uint32_t tCurrentMicros = micros(); + uint32_t tMicrosOfMarkOrSpace32 = tCurrentMicros - TinyIRReceiverControl.LastChangeMicros; + uint16_t tMicrosOfMarkOrSpace = tMicrosOfMarkOrSpace32; + + TinyIRReceiverControl.LastChangeMicros = tCurrentMicros; + + uint8_t tState = TinyIRReceiverControl.IRReceiverState; + +#if defined(LOCAL_TRACE_STATE_MACHINE) + Serial.print(tState); + Serial.print(F(" D=")); + Serial.print(tMicrosOfMarkOrSpace); +// Serial.print(F(" I=")); +// Serial.print(tIRLevel); + Serial.print('|'); +#endif + + if (tIRLevel == LOW) { + /* + * We have a mark here + */ + if (tMicrosOfMarkOrSpace > 2 * TINY_RECEIVER_HEADER_MARK) { + // timeout -> must reset state machine + tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK; + } + if (tState == IR_RECEIVER_STATE_WAITING_FOR_START_MARK) { + // We are at the beginning of the header mark, check timing at the next transition + tState = IR_RECEIVER_STATE_WAITING_FOR_START_SPACE; + TinyIRReceiverControl.Flags = IRDATA_FLAGS_EMPTY; // If we do it here, it saves 4 bytes +#if defined(TRACE) // Do not use LOCAL_TRACE here since sMicrosOfGap is read in a cpp file at TRACE + sMicrosOfGap = tMicrosOfMarkOrSpace32; +#endif +#if defined(ENABLE_NEC2_REPEATS) + // Check for repeat, where full frame is sent again after TINY_RECEIVER_REPEAT_PERIOD ms + // Not required for NEC, where repeats are detected by a special header space duration + // Must use 32 bit arithmetic here! + if (tMicrosOfMarkOrSpace32 < TINY_RECEIVER_MAXIMUM_REPEAT_DISTANCE) { + TinyIRReceiverControl.Flags = IRDATA_FLAGS_IS_REPEAT; + } +#endif + } + + else if (tState == IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK) { + if (tMicrosOfMarkOrSpace >= lowerValue25Percent(TINY_RECEIVER_HEADER_SPACE) + && tMicrosOfMarkOrSpace <= upperValue25Percent(TINY_RECEIVER_HEADER_SPACE)) { + /* + * We had a valid data header space here -> initialize data + */ + TinyIRReceiverControl.IRRawDataBitCounter = 0; +#if (TINY_RECEIVER_BITS > 16) + TinyIRReceiverControl.IRRawData.ULong = 0; +#else + TinyIRReceiverControl.IRRawData.UWord = 0; +#endif + TinyIRReceiverControl.IRRawDataMask = 1; + tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE; +#if !defined(ENABLE_NEC2_REPEATS) + // Check for NEC repeat header + } else if (tMicrosOfMarkOrSpace >= lowerValue25Percent(NEC_REPEAT_HEADER_SPACE) + && tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_REPEAT_HEADER_SPACE) + && TinyIRReceiverControl.IRRawDataBitCounter >= TINY_RECEIVER_BITS) { + /* + * We have a repeat header here and no broken receive before -> set repeat flag + */ + TinyIRReceiverControl.Flags = IRDATA_FLAGS_IS_REPEAT; + tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE; +#endif + } else { + // This parts are optimized by the compiler into jumps to one code :-) + // Wrong length -> reset state + tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK; + } + } + + else if (tState == IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK) { + // Check data space length + if (tMicrosOfMarkOrSpace >= lowerValue50Percent(TINY_RECEIVER_ZERO_SPACE) + && tMicrosOfMarkOrSpace <= upperValue50Percent(TINY_RECEIVER_ONE_SPACE)) { + // We have a valid bit here + tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE; + if (tMicrosOfMarkOrSpace >= 2 * TINY_RECEIVER_UNIT) { + // we received a 1 +#if (TINY_RECEIVER_BITS > 16) + TinyIRReceiverControl.IRRawData.ULong |= TinyIRReceiverControl.IRRawDataMask; +#else + TinyIRReceiverControl.IRRawData.UWord |= TinyIRReceiverControl.IRRawDataMask; +#endif + } else { + // we received a 0 - empty code for documentation + } + // prepare for next bit + TinyIRReceiverControl.IRRawDataMask = TinyIRReceiverControl.IRRawDataMask << 1; + TinyIRReceiverControl.IRRawDataBitCounter++; + } else { + // Wrong length -> reset state + tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK; + } + } else { + // error wrong state for the received level, e.g. if we missed one change interrupt -> reset state + tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK; + } + } + + else { + /* + * We have a space here + */ + if (tState == IR_RECEIVER_STATE_WAITING_FOR_START_SPACE) { + /* + * Check length of header mark here + */ + if (tMicrosOfMarkOrSpace >= lowerValue25Percent(TINY_RECEIVER_HEADER_MARK) + && tMicrosOfMarkOrSpace <= upperValue25Percent(TINY_RECEIVER_HEADER_MARK)) { + tState = IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK; + } else { + // Wrong length of header mark -> reset state + tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK; + } + } + + else if (tState == IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE) { + // Check data mark length + if (tMicrosOfMarkOrSpace >= lowerValue50Percent(TINY_RECEIVER_BIT_MARK) + && tMicrosOfMarkOrSpace <= upperValue50Percent(TINY_RECEIVER_BIT_MARK)) { + /* + * We have a valid mark here, check for transmission complete, i.e. the mark of the stop bit + */ + if (TinyIRReceiverControl.IRRawDataBitCounter >= TINY_RECEIVER_BITS +#if !defined(ENABLE_NEC2_REPEATS) + || (TinyIRReceiverControl.Flags & IRDATA_FLAGS_IS_REPEAT) // Do not check for full length received, if we have a short repeat frame +#endif + ) { + /* + * Code complete -> optionally check parity + */ + // Reset state for new start + tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK; + +#if !defined(DISABLE_PARITY_CHECKS) && (TINY_RECEIVER_ADDRESS_BITS == 16) && TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY + /* + * Check address parity + * Address is sent first and contained in the lower word + */ + if (TinyIRReceiverControl.IRRawData.UBytes[0] != (uint8_t)(~TinyIRReceiverControl.IRRawData.UBytes[1])) { +#if defined(ENABLE_NEC2_REPEATS) + TinyIRReceiverControl.Flags |= IRDATA_FLAGS_PARITY_FAILED; // here we can have the repeat flag already set +#else + TinyIRReceiverControl.Flags = IRDATA_FLAGS_PARITY_FAILED; // here we do not check anything, if we have a repeat +#endif + } +#endif +#if !defined(DISABLE_PARITY_CHECKS) && (TINY_RECEIVER_COMMAND_BITS == 16) && TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY + /* + * Check command parity + */ +#if (TINY_RECEIVER_ADDRESS_BITS > 0) + if (TinyIRReceiverControl.IRRawData.UBytes[2] != (uint8_t)(~TinyIRReceiverControl.IRRawData.UBytes[3])) { +#if defined(ENABLE_NEC2_REPEATS) + TinyIRReceiverControl.Flags |= IRDATA_FLAGS_PARITY_FAILED; +#else + TinyIRReceiverControl.Flags = IRDATA_FLAGS_PARITY_FAILED; +#endif +# if defined(LOCAL_DEBUG) + Serial.print(F("Parity check for command failed. Command=")); + Serial.print(TinyIRReceiverControl.IRRawData.UBytes[2], HEX); + Serial.print(F(" parity=")); + Serial.println(TinyIRReceiverControl.IRRawData.UBytes[3], HEX); +# endif +#else + // No address, so command and parity are in the lowest bytes + if (TinyIRReceiverControl.IRRawData.UBytes[0] != (uint8_t) (~TinyIRReceiverControl.IRRawData.UBytes[1])) { + TinyIRReceiverControl.Flags |= IRDATA_FLAGS_PARITY_FAILED; +# if defined(LOCAL_DEBUG) + Serial.print(F("Parity check for command failed. Command=")); + Serial.print(TinyIRReceiverControl.IRRawData.UBytes[0], HEX); + Serial.print(F(" parity=")); + Serial.println(TinyIRReceiverControl.IRRawData.UBytes[1], HEX); +# endif +#endif + } +#endif + /* + * Call user provided callback here + * The parameter size is dependent of the code variant used in order to save program memory. + * We have 6 cases: 0, 8 bit or 16 bit address, each with 8 or 16 bit command + */ +#if !defined(ARDUINO_ARCH_MBED) && !defined(ESP32) // no Serial etc. in callback for ESP -> no interrupt required, WDT is running! + interrupts(); // enable interrupts, so delay() etc. works in callback +#endif + TinyIRReceiverData.justWritten = true; + TinyIRReceiverData.Flags = TinyIRReceiverControl.Flags; +#if (TINY_RECEIVER_ADDRESS_BITS > 0) +# if TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY + // Here we have 8 bit address + TinyIRReceiverData.Address = TinyIRReceiverControl.IRRawData.UBytes[0]; +# else + // Here we have 16 bit address + TinyIRReceiverData.Address = TinyIRReceiverControl.IRRawData.UWord.LowWord; +# endif +# if TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY + // Here we have 8 bit command + TinyIRReceiverData.Command = TinyIRReceiverControl.IRRawData.UBytes[2]; +# else + // Here we have 16 bit command + TinyIRReceiverData.Command = TinyIRReceiverControl.IRRawData.UWord.HighWord; +# endif + +#else + // Here we have NO address +# if TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY + // Here we have 8 bit command + TinyIRReceiverData.Command = TinyIRReceiverControl.IRRawData.UBytes[0]; +# else + // Here we have 16 bit command + TinyIRReceiverData.Command = TinyIRReceiverControl.IRRawData.UWord; +# endif +#endif +#if defined(USE_CALLBACK_FOR_TINY_RECEIVER) + handleReceivedTinyIRData(); +#endif + + } else { + // not finished yet + tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK; + } + } else { + // Wrong length -> reset state + tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK; + } + } else { + // error wrong state for the received level, e.g. if we missed one change interrupt -> reset state + tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK; + } + } + + TinyIRReceiverControl.IRReceiverState = tState; +#ifdef _IR_MEASURE_TIMING + digitalWriteFast(_IR_TIMING_TEST_PIN, LOW); // 2 clock cycles +#endif +} + +bool isTinyReceiverIdle() { + return (TinyIRReceiverControl.IRReceiverState == IR_RECEIVER_STATE_WAITING_FOR_START_MARK); +} + +/* + * Function to be used as drop in for IrReceiver.decode() + */ +bool TinyReceiverDecode() { + bool tJustWritten = TinyIRReceiverData.justWritten; + if (tJustWritten) { + TinyIRReceiverData.justWritten = false; + } + return tJustWritten; +} + +/* + * Checks if IR_RECEIVE_PIN is connected and high + * @return true, if IR Receiver is attached + */ +bool isIRReceiverAttachedForTinyReceiver() { + pinModeFast(IR_RECEIVE_PIN, OUTPUT); + digitalWriteFast(IR_RECEIVE_PIN, LOW); // discharge pin capacity + pinModeFast(IR_RECEIVE_PIN, INPUT); + return digitalRead(IR_RECEIVE_PIN); // use slow digitalRead here, since the pin capacity is not fully charged again if we use digitalReadFast. +} + +/** + * Sets IR_RECEIVE_PIN mode to INPUT, and if IR_FEEDBACK_LED_PIN is defined, sets feedback LED output mode. + * Then call enablePCIInterruptForTinyReceiver() + */ +bool initPCIInterruptForTinyReceiver() { + pinModeFast(IR_RECEIVE_PIN, INPUT); + +#if !defined(NO_LED_RECEIVE_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN) + pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT); +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH); +# endif +#endif + return enablePCIInterruptForTinyReceiver(); +} + +void printTinyReceiverResultMinimal(Print *aSerial) { +// Print only very short output, since we are in an interrupt context and do not want to miss the next interrupts of the repeats coming soon +#if defined(USE_FAST_PROTOCOL) + aSerial->print(F("C=0x")); +#else + aSerial->print(F("A=0x")); + aSerial->print(TinyIRReceiverData.Address, HEX); + aSerial->print(F(" C=0x")); +#endif + aSerial->print(TinyIRReceiverData.Command, HEX); + if (TinyIRReceiverData.Flags == IRDATA_FLAGS_IS_REPEAT) { + aSerial->print(F(" R")); + } +#if !defined(DISABLE_PARITY_CHECKS) + if (TinyIRReceiverData.Flags == IRDATA_FLAGS_PARITY_FAILED) { + aSerial->print(F(" P")); + } +#endif + aSerial->println(); +} + +#if defined (LOCAL_DEBUG_ATTACH_INTERRUPT) && !defined(STR) +// Helper macro for getting a macro definition as string +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#endif + +/************************************************** + * Pin to interrupt mapping for different platforms + **************************************************/ +#if defined(__AVR_ATtiny816__) || defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) +#define USE_ATTACH_INTERRUPT_DIRECT + +#elif !defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT) +// Default for all NON AVR platforms +#define USE_ATTACH_INTERRUPT + +#else +# if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) +#define USE_PCIE + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +# if defined(ARDUINO_AVR_DIGISPARKPRO) +# if (IR_RECEIVE_PIN == 3) +#define USE_INT0 +# elif (IR_RECEIVE_PIN == 9) +#define USE_INT1 +# else +# error "IR_RECEIVE_PIN must be 9 or 3." +# endif // if (IR_RECEIVE_PIN == 9) +# else // defined(ARDUINO_AVR_DIGISPARKPRO) +# if (IR_RECEIVE_PIN == 14) +#define USE_INT0 +# elif (IR_RECEIVE_PIN == 3) +#define USE_INT1 +# else +# error "IR_RECEIVE_PIN must be 14 or 3." +# endif // if (IR_RECEIVE_PIN == 14) +# endif + +# elif (defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)) +# if (IR_RECEIVE_PIN == 21) +#define USE_INT0 +# elif (IR_RECEIVE_PIN == 20) +#define USE_INT1 +# else +#warning "No pin mapping for IR_RECEIVE_PIN to interrupt found -> attachInterrupt() is used now." +#define USE_ATTACH_INTERRUPT +# endif + +# else // defined(__AVR_ATtiny25__) +/* + * ATmegas + ATtiny88 here + */ +# if (IR_RECEIVE_PIN == 2) +#define USE_INT0 +# elif (IR_RECEIVE_PIN == 3) +#define USE_INT1 + +# elif IR_RECEIVE_PIN == 4 || IR_RECEIVE_PIN == 5 || IR_RECEIVE_PIN == 6 || IR_RECEIVE_PIN == 7 + //ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 20 to 23 for port PD4 to PD7 (Arduino pin 4 to 7) +#define USE_PCINT2 +# elif IR_RECEIVE_PIN == 8 || IR_RECEIVE_PIN == 9 || IR_RECEIVE_PIN == 10 || IR_RECEIVE_PIN == 11 || IR_RECEIVE_PIN == 12 || IR_RECEIVE_PIN == 13 + //ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 0 to 5 for port PB0 to PB5 (Arduino pin 8 to 13) +#define USE_PCINT0 +# elif IR_RECEIVE_PIN == A0 || IR_RECEIVE_PIN == A1 || IR_RECEIVE_PIN == A2 || IR_RECEIVE_PIN == A3 || IR_RECEIVE_PIN == A4 || IR_RECEIVE_PIN == A5 + //ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 8 to 13 for port PC0 to PC5 (Arduino pin A0 to A5) +#define USE_PCINT1 + +# else +#warning "No pin mapping for IR_RECEIVE_PIN to interrupt found -> attachInterrupt() is used now." +#define USE_ATTACH_INTERRUPT +# endif // if (IR_RECEIVE_PIN == 2) +# endif // defined(__AVR_ATtiny25__) +#endif // ! defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT) + +/** + * Initializes hardware interrupt generation according to IR_RECEIVE_PIN or use attachInterrupt() function. + * @return true if interrupt was successfully enabled + */ +bool enablePCIInterruptForTinyReceiver() { +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) + pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT); +#endif + +#if defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT) +# if defined(USE_ATTACH_INTERRUPT) +# if defined(NOT_AN_INTERRUPT) // check if IDE has defined the check of digitalPinToInterrupt + if(digitalPinToInterrupt(IR_RECEIVE_PIN) == NOT_AN_INTERRUPT){ + return false; + } +# endif + // costs 112 bytes program memory + 4 bytes RAM +# if defined(ARDUINO_ARCH_SAMD) // see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ paragraph: Syntax + attachInterrupt(IR_RECEIVE_PIN, IRPinChangeInterruptHandler, CHANGE); // no extra pin mapping here :-( +# else + attachInterrupt(digitalPinToInterrupt(IR_RECEIVE_PIN), IRPinChangeInterruptHandler, CHANGE); +# endif +# else + // USE_ATTACH_INTERRUPT_DIRECT here, only defined for ATtinies *16, see above + // 2.2 us more than version configured with macros and not compatible + attachInterrupt(IR_RECEIVE_PIN, IRPinChangeInterruptHandler, CHANGE); // no extra pin mapping here +# endif + +# if defined(LOCAL_DEBUG_ATTACH_INTERRUPT) + Serial.println(F("Use attachInterrupt for pin=" STR(IR_RECEIVE_PIN))); +# endif +#else +# if defined(LOCAL_DEBUG_ATTACH_INTERRUPT) + Serial.println(F("Use static interrupt for pin=" STR(IR_RECEIVE_PIN))); +# endif + +# if defined(USE_INT0) + // interrupt on any logical change + EICRA |= _BV(ISC00); + // clear interrupt bit + EIFR |= 1 << INTF0; + // enable interrupt on next change + EIMSK |= 1 << INT0; + +# elif defined(USE_INT1) + EICRA |= _BV(ISC10); +// clear interrupt bit + EIFR |= 1 << INTF1; +// enable interrupt on next change + EIMSK |= 1 << INT1; + +# elif defined(USE_PCIE) // For ATtiny85 etc. + // use PinChangeInterrupt no INT0 for pin PB2 + PCMSK = _BV(IR_RECEIVE_PIN); + // clear interrupt bit + GIFR |= 1 << PCIF; + // enable interrupt on next change + GIMSK |= 1 << PCIE; + +# elif defined(USE_PCINT0) + PCICR |= _BV(PCIE0); + PCMSK0 = digitalPinToBitMask(IR_RECEIVE_PIN); +# elif defined(USE_PCINT1) + PCICR |= _BV(PCIE1); + PCMSK1 = digitalPinToBitMask(IR_RECEIVE_PIN); +# elif defined(USE_PCINT2) + PCICR |= _BV(PCIE2); + PCMSK2 = digitalPinToBitMask(IR_RECEIVE_PIN); +# else + return false; +# endif +#endif // defined(USE_ATTACH_INTERRUPT) + return true; +} + +void disablePCIInterruptForTinyReceiver() { +#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) + pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT); +#endif + +#if defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT) +# if defined(USE_ATTACH_INTERRUPT) + detachInterrupt(digitalPinToInterrupt(IR_RECEIVE_PIN)); +# else + detachInterrupt(IR_RECEIVE_PIN); +# endif + +#else +# if defined(USE_INT0) + // clear interrupt bit + EIFR |= 1 << INTF0; + // disable interrupt on next change + EIMSK &= ~(1 << INT0); + +# elif defined(USE_INT1) + // clear interrupt bit + EIFR |= 1 << INTF1; + // disable interrupt on next change + EIMSK &= ~(1 << INT1); + +# elif defined(USE_PCIE) // For ATtiny85 etc. + // clear interrupt bit + GIFR |= 1 << PCIF; + // disable interrupt on next change + GIMSK &= ~(1 << PCIE); + +# elif defined(USE_PCINT0) + PCICR &= ~(_BV(PCIE0)); +# elif defined(USE_PCINT1) + PCICR &= ~(_BV(PCIE1)); +# elif defined(USE_PCINT2) + PCICR &= ~(_BV(PCIE2)); + +# endif +#endif // defined(USE_ATTACH_INTERRUPT) +} + +/* + * Specify the right INT0, INT1 or PCINT0 interrupt vector according to different pins and cores. + * The default value of TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT is set in TinyIRReceiver.h + */ +#if !(defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT)) +# if defined(USE_INT0) +ISR(INT0_vect) + +# elif defined(USE_INT1) +ISR(INT1_vect) + +# elif defined(USE_PCIE) // For ATtiny85 etc. +// on ATtinyX5 we do not have a INT1_vect but we can use the PCINT0_vect +ISR(PCINT0_vect) + +# elif defined(USE_PCINT0) +ISR(PCINT0_vect) +# elif defined(USE_PCINT1) +ISR(PCINT1_vect) +# elif defined(USE_PCINT2) +ISR(PCINT2_vect) +# else +void dummyFunctionToAvoidCompilerErrors() +# endif +{ + IRPinChangeInterruptHandler(); +} +#endif // !(defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT)) + +/** @}*/ + +#if defined(LOCAL_DEBUG_ATTACH_INTERRUPT) +#undef LOCAL_DEBUG_ATTACH_INTERRUPT +#endif +#if defined(LOCAL_TRACE_STATE_MACHINE) +#undef LOCAL_TRACE_STATE_MACHINE +#endif + +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _TINY_IR_RECEIVER_HPP diff --git a/src/TinyIRSender.hpp b/src/TinyIRSender.hpp new file mode 100644 index 000000000..be0d07ef7 --- /dev/null +++ b/src/TinyIRSender.hpp @@ -0,0 +1,417 @@ +/* + * TinyIRSender.hpp + * + * Sends IR protocol data of NEC and FAST protocol using bit banging. + * NEC is the protocol of most cheap remote controls for Arduino. + * + * The FAST protocol is a proprietary modified JVC protocol without address, with parity and with a shorter header. + * FAST Protocol characteristics: + * - Bit timing is like NEC or JVC + * - The header is shorter, 3156 vs. 12500 + * - No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command, + * leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms. + * - Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance. + * + * + * This file is part of IRMP https://github.com/IRMP-org/IRMP. + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2022-2025 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +#ifndef _TINY_IR_SENDER_HPP +#define _TINY_IR_SENDER_HPP + +#include + +//#define ENABLE_NEC2_REPEATS // Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat. + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif +//#define NO_LED_SEND_FEEDBACK_CODE // Disables the LED feedback code for receive. +//#define IR_FEEDBACK_LED_PIN 12 // Use this, to disable use of LED_BUILTIN definition for IR_FEEDBACK_LED_PIN +#include "TinyIR.h" // Defines protocol timings + +#include "digitalWriteFast.h" +/** \addtogroup TinySender Minimal sender for NEC and FAST protocol + * @{ + */ + +#if !defined(IR_SEND_PIN) +#warning "IR_SEND_PIN is not defined, so it is set to 3" +#define IR_SEND_PIN 3 +#endif +/* + * Generate 38 kHz IR signal by bit banging + */ +void sendMark(uint8_t aSendPin, unsigned int aMarkMicros) { + unsigned long tStartMicros = micros(); + unsigned long tNextPeriodEnding = tStartMicros; + unsigned long tMicros; + do { + /* + * Generate pulse + */ + noInterrupts(); // do not let interrupts extend the short on period + digitalWriteFast(aSendPin, HIGH); + delayMicroseconds(8); // 8 us for a 30 % duty cycle for 38 kHz + digitalWriteFast(aSendPin, LOW); + interrupts(); // Enable interrupts - to keep micros correct- for the longer off period 3.4 us until receive ISR is active (for 7 us + pop's) + + /* + * PWM pause timing and end check + * Minimal pause duration is 4.3 us + */ + tNextPeriodEnding += 26; // for 38 kHz + do { + tMicros = micros(); // we have only 4 us resolution for AVR @16MHz + /* + * Exit the forever loop if aMarkMicros has reached + */ + unsigned int tDeltaMicros = tMicros - tStartMicros; +#if defined(__AVR__) + // Just getting variables and check for end condition takes minimal 3.8 us + if (tDeltaMicros >= aMarkMicros - (112 / (F_CPU / MICROS_IN_ONE_SECOND))) { // To compensate for call duration - 112 is an empirical value +#else + if (tDeltaMicros >= aMarkMicros) { + #endif + return; + } + } while (tMicros < tNextPeriodEnding); + } while (true); +} + +/* + * Send NEC with 16 bit address and command, even if aCommand < 0x100 (I.E. ONKYO) + * @param aAddress - The 16 bit address to send. + * @param aCommand - The 16 bit command to send. + * @param aNumberOfRepeats - Number of repeats send at a period of 110 ms. + * @param aSendNEC2Repeats - Instead of sending the NEC special repeat code, send the original frame for repeat. + */ +void sendONKYO(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendNEC2Repeats) { + pinModeFast(aSendPin, OUTPUT); + +#if !defined(NO_LED_SEND_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN) + pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT); +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(IR_FEEDBACK_LED_PIN, LOW); +# else + digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH); +# endif +#endif + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + unsigned long tStartOfFrameMillis = millis(); + + sendMark(aSendPin, NEC_HEADER_MARK); + if ((!aSendNEC2Repeats) && (tNumberOfCommands < aNumberOfRepeats + 1)) { + // send the NEC special repeat + delayMicroseconds (NEC_REPEAT_HEADER_SPACE); // - 2250 + } else { + // send header + delayMicroseconds (NEC_HEADER_SPACE); + LongUnion tData; + tData.UWord.LowWord = aAddress; + tData.UWord.HighWord = aCommand; + // Send data + for (uint_fast8_t i = 0; i < NEC_BITS; ++i) { + sendMark(aSendPin, NEC_BIT_MARK); // constant mark length + if (tData.ULong & 1) { + delayMicroseconds (NEC_ONE_SPACE); + } else { + delayMicroseconds (NEC_ZERO_SPACE); + } + tData.ULong >>= 1; // shift command for next bit + } + } // send stop bit + sendMark(aSendPin, NEC_BIT_MARK); + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + /* + * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration. + */ + auto tFrameDurationMillis = millis() - tStartOfFrameMillis; + if (NEC_REPEAT_PERIOD / 1000 > tFrameDurationMillis) { + delay(NEC_REPEAT_PERIOD / 1000 - tFrameDurationMillis); + } + } + } +#if !defined(NO_LED_SEND_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN) + pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT); +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH); +# else + digitalWriteFast(IR_FEEDBACK_LED_PIN, LOW); +# endif +#endif +} + +/* + * Send NEC with 8 or 16 bit address or command depending on the values of aAddress and aCommand. + * @param aAddress - If aAddress < 0x100 send 8 bit address and 8 bit inverted address, else send 16 bit address. + * @param aCommand - If aCommand < 0x100 send 8 bit command and 8 bit inverted command, else send 16 bit command. + * @param aNumberOfRepeats - Number of repeats send at a period of 110 ms. + * @param aSendNEC2Repeats - Instead of sending the NEC special repeat code, send the original frame for repeat. + */ +void sendNECMinimal(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats) { + sendNEC(aSendPin, aAddress, aCommand, aNumberOfRepeats); // sendNECMinimal() is deprecated +} +void sendNEC(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendNEC2Repeats) { + pinModeFast(aSendPin, OUTPUT); + +#if !defined(NO_LED_SEND_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN) + pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT); +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(IR_FEEDBACK_LED_PIN, LOW); +# else + digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH); +# endif +#endif + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + unsigned long tStartOfFrameMillis = millis(); + + sendMark(aSendPin, NEC_HEADER_MARK); + if ((!aSendNEC2Repeats) && (tNumberOfCommands < aNumberOfRepeats + 1)) { + // send the NEC special repeat + delayMicroseconds (NEC_REPEAT_HEADER_SPACE); // - 2250 + } else { + // send header + delayMicroseconds (NEC_HEADER_SPACE); + LongUnion tData; + /* + * The compiler is intelligent and removes the code for "(aAddress > 0xFF)" if we are called with an uint8_t address :-). + * Using an uint16_t address requires additional 28 bytes program memory. + */ + if (aAddress > 0xFF) { + tData.UWord.LowWord = aAddress; + } else { + tData.UByte.LowByte = aAddress; // LSB first + tData.UByte.MidLowByte = ~aAddress; + } + if (aCommand > 0xFF) { + tData.UWord.HighWord = aCommand; + } else { + tData.UByte.MidHighByte = aCommand; + tData.UByte.HighByte = ~aCommand; // LSB first + } + // Send data + for (uint_fast8_t i = 0; i < NEC_BITS; ++i) { + sendMark(aSendPin, NEC_BIT_MARK); // constant mark length + + if (tData.ULong & 1) { + delayMicroseconds (NEC_ONE_SPACE); + } else { + delayMicroseconds (NEC_ZERO_SPACE); + } + tData.ULong >>= 1; // shift command for next bit + } + } // send stop bit + sendMark(aSendPin, NEC_BIT_MARK); + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + /* + * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration. + */ + auto tFrameDurationMillis = millis() - tStartOfFrameMillis; + if (NEC_REPEAT_PERIOD / 1000 > tFrameDurationMillis) { + delay(NEC_REPEAT_PERIOD / 1000 - tFrameDurationMillis); + } + } + } +#if !defined(NO_LED_SEND_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN) + pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT); +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH); +# else + digitalWriteFast(IR_FEEDBACK_LED_PIN, LOW); +# endif +#endif +} + +/* + * Send Extended NEC with a forced 16 bit address and 8 or 16 bit command depending on the value of aCommand. + * @param aAddress - Send 16 bit address. + * @param aCommand - If aCommand < 0x100 send 8 bit command and 8 bit inverted command, else send 16 bit command. + * @param aNumberOfRepeats - Number of repeats send at a period of 110 ms. + * @param aSendNEC2Repeats - Instead of sending the NEC special repeat code, send the original frame for repeat. + */ +void sendExtendedNEC(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendNEC2Repeats) { + pinModeFast(aSendPin, OUTPUT); + +#if !defined(NO_LED_SEND_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN) + pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT); +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(IR_FEEDBACK_LED_PIN, LOW); +# else + digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH); +# endif +#endif + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + unsigned long tStartOfFrameMillis = millis(); + + sendMark(aSendPin, NEC_HEADER_MARK); + if ((!aSendNEC2Repeats) && (tNumberOfCommands < aNumberOfRepeats + 1)) { + // send the NEC special repeat + delayMicroseconds (NEC_REPEAT_HEADER_SPACE); // - 2250 + } else { + // send header + delayMicroseconds (NEC_HEADER_SPACE); + LongUnion tData; + tData.UWord.LowWord = aAddress; + if (aCommand > 0xFF) { + tData.UWord.HighWord = aCommand; + } else { + tData.UByte.MidHighByte = aCommand; + tData.UByte.HighByte = ~aCommand; // LSB first + } + // Send data + for (uint_fast8_t i = 0; i < NEC_BITS; ++i) { + sendMark(aSendPin, NEC_BIT_MARK); // constant mark length + + if (tData.ULong & 1) { + delayMicroseconds (NEC_ONE_SPACE); + } else { + delayMicroseconds (NEC_ZERO_SPACE); + } + tData.ULong >>= 1; // shift command for next bit + } + } // send stop bit + sendMark(aSendPin, NEC_BIT_MARK); + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + /* + * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration. + */ + auto tFrameDurationMillis = millis() - tStartOfFrameMillis; + if (NEC_REPEAT_PERIOD / 1000 > tFrameDurationMillis) { + delay(NEC_REPEAT_PERIOD / 1000 - tFrameDurationMillis); + } + } + } +#if !defined(NO_LED_SEND_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN) + pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT); +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH); +# else + digitalWriteFast(IR_FEEDBACK_LED_PIN, LOW); +# endif +#endif +} + +/* + * LSB first, send header, command, inverted command and stop bit + */ +void sendFast8BitAndParity(uint8_t aSendPin, uint8_t aCommand, uint_fast8_t aNumberOfRepeats) { + sendFAST(aSendPin, aCommand, aNumberOfRepeats); +} + +/* + * LSB first, send header, 16 bit command or 8 bit command, inverted command and stop bit + */ +void sendFAST(uint8_t aSendPin, uint16_t aCommand, uint_fast8_t aNumberOfRepeats) { + pinModeFast(aSendPin, OUTPUT); + +#if !defined(NO_LED_SEND_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN) + pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT); +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(IR_FEEDBACK_LED_PIN, LOW); +# else + digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH); +# endif +#endif + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + unsigned long tStartOfFrameMillis = millis(); + + // send header + sendMark(aSendPin, FAST_HEADER_MARK); + delayMicroseconds(FAST_HEADER_SPACE); + uint16_t tData; + /* + * The compiler is intelligent and removes the code for "(aCommand > 0xFF)" if we are called with an uint8_t command :-). + * Using an uint16_t command requires additional 56 bytes program memory. + */ + if (aCommand > 0xFF) { + tData = aCommand; + } else { + tData = aCommand | (((uint8_t) (~aCommand)) << 8); // LSB first + } + // Send data + for (uint_fast8_t i = 0; i < FAST_BITS; ++i) { + sendMark(aSendPin, FAST_BIT_MARK); // constant mark length + + if (tData & 1) { + delayMicroseconds(FAST_ONE_SPACE); + } else { + delayMicroseconds(FAST_ZERO_SPACE); + } + tData >>= 1; // shift command for next bit + } + // send stop bit + sendMark(aSendPin, FAST_BIT_MARK); + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + /* + * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration. + */ + auto tFrameDurationMillis = millis() - tStartOfFrameMillis; + if (FAST_REPEAT_PERIOD / 1000 > tFrameDurationMillis) { + delay(FAST_REPEAT_PERIOD / 1000 - tFrameDurationMillis); + } + } + } +#if !defined(NO_LED_SEND_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN) + pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT); +# if defined(FEEDBACK_LED_IS_ACTIVE_LOW) + digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH); +# else + digitalWriteFast(IR_FEEDBACK_LED_PIN, LOW); +# endif +#endif +} + +/** @}*/ + +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _TINY_IR_SENDER_HPP diff --git a/src/ac_LG.h b/src/ac_LG.h new file mode 100644 index 000000000..4f56de7b7 --- /dev/null +++ b/src/ac_LG.h @@ -0,0 +1,138 @@ +/* + * ac_LG.h + * + * Contains definitions for receiving and sending LG air conditioner IR Protocol + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2021 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +// see also: https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h +#ifndef _AC_LG_H +#define _AC_LG_H + +#include + +/** \addtogroup Airconditoners Air conditioner special code + * @{ + */ + +#define LG_ADDRESS 0x88 + +/* + * The basic IR command codes + * Parts of the codes (especially the lower nibbles) may be modified to contain + * additional information like temperature, fan speed and minutes. + */ +#define LG_SWITCH_ON_MASK 0x0800 // This bit is masked if we switch Power on +#define LG_MODE_COOLING 0x0800 // Temperature and fan speed in lower nibbles +#define LG_MODE_DEHUMIDIFIYING 0x0990 // sets also temperature to 24 and fan speed to 0 +#define LG_MODE_FAN 0x0A30 // sets also temperature to 18 +#define LG_MODE_AUTO 0x0B00 // The remote initially sets also temperature to 22 and fan speed to 4 +#define LG_MODE_HEATING 0x0C00 // Temperature and fan speed in lower nibbles +#define LG_ENERGY_SAVING_ON 0x1004 +#define LG_ENERGY_SAVING_OFF 0x1005 +#define LG_JET_ON 0x1008 +#define LG_WALL_SWING_ON 0x1314 +#define LG_WALL_SWING_OFF 0x1315 +#define LG_SWING_ON 0x1316 // not verified, for AKB73757604 +#define LG_SWING_OFF 0x1317 // not verified, for AKB73757604 +#define LG_TIMER_ON 0x8000 // relative minutes in lower nibbles +#define LG_TIMER_OFF 0x9000 // relative minutes in lower nibbles +#define LG_SLEEP 0xA000 // relative minutes in lower nibbles +#define LG_CLEAR_ALL 0xB000 // Timers and sleep +#define LG_POWER_DOWN 0xC005 +#define LG_LIGHT 0xC00A +#define LG_AUTO_CLEAN_ON 0xC00B +#define LG_AUTO_CLEAN_OFF 0xC00C + +/* + * Commands as printed in menu and uses as first parameter for sendCommandAndParameter + */ +#define LG_COMMAND_OFF '0' +#define LG_COMMAND_ON '1' +#define LG_COMMAND_SWING 's' +#define LG_COMMAND_AUTO_CLEAN 'a' +#define LG_COMMAND_JET 'j' +#define LG_COMMAND_ENERGY 'e' +#define LG_COMMAND_LIGHT 'l' +#define LG_COMMAND_FAN_SPEED 'f' +#define LG_COMMAND_TEMPERATURE 't' +#define LG_COMMAND_TEMPERATURE_PLUS '+' +#define LG_COMMAND_TEMPERATURE_MINUS '-' +#define LG_COMMAND_MODE 'm' +#define LG_COMMAND_SLEEP 'S' +#define LG_COMMAND_TIMER_ON 'T' +#define LG_COMMAND_TIMER_OFF 'O' +#define LG_COMMAND_CLEAR_ALL 'C' + +/* + * The modes are encoded as character values for easy printing :-) + */ +#define AC_MODE_COOLING 'c' +#define AC_MODE_DEHUMIDIFIYING 'd' +#define AC_MODE_FAN 'f' +#define AC_MODE_AUTO 'a' +#define AC_MODE_HEATING 'h' + +// see https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h +union LGProtocol { + uint32_t raw; ///< The state of the IR remote in IR code form. + struct { + uint32_t Checksum :4; + uint32_t Fan :3; + uint32_t FanExt :1; + uint32_t Temp :4; + uint32_t Mode :4; // highest bit 1 => Set temperature and ventilation by mode + uint32_t Function :3; + uint32_t SwitchOnMask :1; /* Content is 0 when switching from off to on */ + uint32_t Signature :8; /* Content is 0x88, LG_ADDRESS */ + }; +}; + +class Aircondition_LG { +public: + bool sendCommandAndParameter(char aCommand, int aParameter); + void setType(bool aIsWallType); + void printMenu(Print *aSerial); + void sendIRCommand(uint16_t aCommand); + void sendTemperatureFanSpeedAndMode(); + /* + * Internal state of the air condition + */ +#define LG_IS_WALL_TYPE true +#define LG_IS_TOWER_TYPE false + bool ACIsWallType; // false : TOWER, true : WALL + bool PowerIsOn; + + // These value are encoded and sent by AC_LG_SendCommandAndParameter() + uint8_t FanIntensity = 1; // 0 -> low, 4 high, 5 -> cycle + uint8_t Temperature = 22; // temperature : 18 ~ 30 + uint8_t Mode = AC_MODE_COOLING; + bool useLG2Protocol = false; +}; + +/** @}*/ +#endif // _AC_LG_H diff --git a/src/ac_LG.hpp b/src/ac_LG.hpp new file mode 100644 index 000000000..d5037c2d7 --- /dev/null +++ b/src/ac_LG.hpp @@ -0,0 +1,322 @@ +/* + * ac_LG.hpp + * + * Contains functions for sending LG air conditioner IR Protocol + * There is no state plausibility check, e.g. you can send fan speed in Mode D and change temperature in mode F + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2021-2022 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _AC_LG_HPP +#define _AC_LG_HPP +#include + +#if defined(INFO) && !defined(LOCAL_INFO) +#define LOCAL_INFO +#else +//#define LOCAL_INFO // This enables info output only for this file +#endif +//#define DEBUG // for more output from the LG-AC driver. +#include "IRremoteInt.h" +#include "ac_LG.h" // useful constants +#include "LongUnion.h" + +/** \addtogroup Airconditoners Air conditioner special code + * @{ + */ +/* + * LG remote measurements: Type AKB73315611, Ver1.1 from 2011.03.01 + * Internal crystal: 4 MHz + * Header: 8.9 ms mark 4.15 ms space + * Data: 500 / 540 and 500 / 1580; + * Clock is nor synchronized with gate so you have 19 and sometimes 19 and a spike pulses for mark + * Duty: 9 us on 17 us off => around 33 % duty + * NO REPEAT: If value like temperature has changed during long press, the last value is send at button release + * If you do a double press -tested with the fan button-, the next value can be sent after 118 ms + */ +#define SIZE_OF_FAN_SPEED_MAPPING_TABLE 4 +const int AC_FAN_TOWER[SIZE_OF_FAN_SPEED_MAPPING_TABLE] = { 0, 4, 6, 6 }; // last dummy entry to avoid out of bounds access +const int AC_FAN_WALL[SIZE_OF_FAN_SPEED_MAPPING_TABLE] = { 0, 2, 4, 5 }; // 0 -> low, 4 high, 5 -> cycle + +void Aircondition_LG::setType(bool aIsWallType) { + ACIsWallType = aIsWallType; +#if defined(LOCAL_INFO) + Serial.print(F("Set wall type to ")); + Serial.println(aIsWallType); +#endif +} + +void Aircondition_LG::printMenu(Print *aSerial) { + aSerial->println(); + aSerial->println(); + aSerial->println(F("Type command and optional parameter without a separator")); + aSerial->println(F("0 Off")); + aSerial->println(F("1 On")); + aSerial->println(F("s Swing <0 or 1>")); + aSerial->println(F("a Auto clean <0 or 1>")); + aSerial->println(F("j Jet on")); + aSerial->println(F("e Energy saving <0 or 1>")); + aSerial->println(F("l Lights toggle")); + aSerial->println(F("f Fan speed <0 to 2 or 3 for cycle>")); + aSerial->println(F("t Temperature <18 to 30> degree")); + aSerial->println(F("+ Temperature + 1")); + aSerial->println(F("- Temperature - 1")); + aSerial->println(F("m ")); + aSerial->println(F("S Sleep after <0 to 420> minutes")); + aSerial->println(F("T Timer on after <0 to 1439> minutes")); + aSerial->println(F("O Timer off after <0 to 1439> minutes")); + aSerial->println(F("C Clear all timer and sleep")); + aSerial->println(F("e.g. \"s1\" or \"t23\" or \"mc\" or \"O60\" or \"+\"")); + aSerial->println(F("No plausibility check is made!")); + aSerial->println(); +} + +/* + * Send repeat + * Repeat commands should be sent in a 110 ms raster. + * @param aCommand one of LG_COMMAND_OFF, LG_COMMAND_ON etc. + */ +bool Aircondition_LG::sendCommandAndParameter(char aCommand, int aParameter) { + // Commands without parameter + switch (aCommand) { + case LG_COMMAND_OFF: // off + sendIRCommand(LG_POWER_DOWN); + PowerIsOn = false; + return true; + + case LG_COMMAND_ON: // on + PowerIsOn = false; // set to false in order to suppress on bit + sendTemperatureFanSpeedAndMode(); + return true; + + case LG_COMMAND_JET: + IR_DEBUG_PRINTLN(F("Send jet on")); + sendIRCommand(LG_JET_ON); + return true; + + case LG_COMMAND_LIGHT: + sendIRCommand(LG_LIGHT); + return true; + + case LG_COMMAND_CLEAR_ALL: + sendIRCommand(LG_CLEAR_ALL); + return true; + + case LG_COMMAND_TEMPERATURE_PLUS: + if (18 <= Temperature && Temperature <= 29) { + Temperature++; + sendTemperatureFanSpeedAndMode(); + } else { + return false; + } + return true; + + case LG_COMMAND_TEMPERATURE_MINUS: + if (19 <= Temperature && Temperature <= 30) { + Temperature--; + sendTemperatureFanSpeedAndMode(); + } else { + return false; + } + return true; + + } + + PowerIsOn = true; + + /* + * Now the commands which require a parameter + */ + if (aParameter < 0) { + IR_DEBUG_PRINT(F("Error: Parameter is less than 0")); + return false; + } + switch (aCommand) { + + case LG_COMMAND_MODE: + Mode = aParameter + '0'; + sendTemperatureFanSpeedAndMode(); + break; + + case LG_COMMAND_SWING: + IR_DEBUG_PRINT(F("Send air swing=")); + IR_DEBUG_PRINTLN(aParameter); + if (ACIsWallType) { + if (aParameter) { + sendIRCommand(LG_WALL_SWING_ON); + } else { + sendIRCommand(LG_WALL_SWING_OFF); + } + } else { + if (aParameter) { + sendIRCommand(LG_SWING_ON); + } else { + sendIRCommand(LG_SWING_OFF); + } + } + break; + + case LG_COMMAND_AUTO_CLEAN: + IR_DEBUG_PRINT(F("Send auto clean=")); + IR_DEBUG_PRINTLN(aParameter); + if (aParameter) { + sendIRCommand(LG_AUTO_CLEAN_ON); + } else { + sendIRCommand(LG_AUTO_CLEAN_OFF); + } + break; + + case LG_COMMAND_ENERGY: + IR_DEBUG_PRINT(F("Send energy saving on=")); + IR_DEBUG_PRINTLN(aParameter); + if (aParameter) { + sendIRCommand(LG_ENERGY_SAVING_ON); + } else { + sendIRCommand(LG_ENERGY_SAVING_OFF); + } + break; + + case LG_COMMAND_FAN_SPEED: + if (aParameter < SIZE_OF_FAN_SPEED_MAPPING_TABLE) { + FanIntensity = aParameter; + sendTemperatureFanSpeedAndMode(); + } else { + return false; + } + break; + + case LG_COMMAND_TEMPERATURE: + if (18 <= aParameter && aParameter <= 30) { + Temperature = aParameter; + sendTemperatureFanSpeedAndMode(); + } else { + return false; + } + break; + + case LG_COMMAND_SLEEP: + // 420 = maximum I have recorded + if (aParameter <= 420) { + sendIRCommand(LG_SLEEP + aParameter); + } else { + return false; + } + break; + + case LG_COMMAND_TIMER_ON: + // 1440 = minutes of a day + if (aParameter <= 1439) { + sendIRCommand(LG_TIMER_ON + aParameter); + } else { + return false; + } + break; + + case LG_COMMAND_TIMER_OFF: + if (aParameter <= 1439) { + sendIRCommand(LG_TIMER_OFF + aParameter); + } else { + return false; + } + break; + + default: + return false; + } + return true; +} + +void Aircondition_LG::sendIRCommand(uint16_t aCommand) { + +#if defined(LOCAL_INFO) + Serial.print(F("Send code=0x")); + Serial.print(aCommand, HEX); + Serial.print(F(" | 0b")); + Serial.println(aCommand, BIN); +#endif + + IrSender.sendLG2((uint8_t) LG_ADDRESS, aCommand, 0); +} + +/* + * Takes values from static variables + */ +void Aircondition_LG::sendTemperatureFanSpeedAndMode() { + + uint8_t tTemperature = Temperature; +#if defined(LOCAL_INFO) + Serial.print(F("Send temperature=")); + Serial.print(tTemperature); + Serial.print(F(" fan intensity=")); + Serial.print(FanIntensity); + Serial.print(F(" mode=")); + Serial.println((char )Mode); +#endif + + WordUnion tIRCommand; + tIRCommand.UWord = 0; + + // Temperature is coded in the upper nibble of the LowByte + tIRCommand.UByte.LowByte = ((tTemperature - 15) << 4); // 16 -> 0x00, 18 -> 0x30, 30 -> 0xF0 + + // Fan intensity is coded in the lower nibble of the LowByte + if (ACIsWallType) { + tIRCommand.UByte.LowByte |= AC_FAN_WALL[FanIntensity]; + } else { + tIRCommand.UByte.LowByte |= AC_FAN_TOWER[FanIntensity]; + } + + switch (Mode) { + case AC_MODE_COOLING: + tIRCommand.UByte.HighByte = LG_MODE_COOLING >> 8; + break; + case AC_MODE_HEATING: + tIRCommand.UByte.HighByte = LG_MODE_HEATING >> 8; + break; + case AC_MODE_AUTO: + tIRCommand.UByte.HighByte = LG_MODE_AUTO >> 8; + break; + case AC_MODE_FAN: + tTemperature = 18; + tIRCommand.UByte.HighByte = LG_MODE_FAN >> 8; + break; + case AC_MODE_DEHUMIDIFIYING: + tIRCommand.UWord = LG_MODE_DEHUMIDIFIYING; + break; + default: + break; + } + if (!PowerIsOn) { + // switch on requires masked bit + tIRCommand.UByte.HighByte &= ~(LG_SWITCH_ON_MASK >> 8); + } + PowerIsOn = true; + + sendIRCommand(tIRCommand.UWord); +} + +/** @}*/ +#endif // _AC_LG_HPP diff --git a/src/digitalWriteFast.h b/src/digitalWriteFast.h new file mode 100644 index 000000000..faeaa5afd --- /dev/null +++ b/src/digitalWriteFast.h @@ -0,0 +1,430 @@ +/* + * digitalWriteFast.h + * + * Optimized digital functions for AVR microcontrollers + * by Watterott electronic (www.watterott.com) + * based on https://code.google.com/p/digitalwritefast + * + * The value of DigitalReadFast() is the content of the input register e.g. 0x04 for pin2 and NOT always 0 or 1. + * + * License: BSD 3-Clause License (https://opensource.org/licenses/BSD-3-Clause) + */ + +#ifndef __digitalWriteFast_h_ +#define __digitalWriteFast_h_ 1 + +//#define THROW_ERROR_IF_NOT_FAST // If activated, an error is thrown if pin is not a compile time constant +void NonConstantsUsedForPinModeFast( void ) __attribute__ (( error("Parameter for pinModeFast() function is not constant") )); +void NonConstantsUsedForDigitalWriteFast( void ) __attribute__ (( error("Parameter for digitalWriteFast() function is not constant") )); +void NonConstantsUsedForDigitalToggleFast( void ) __attribute__ (( error("Parameter for digitalToggleFast() function is not constant") )); +int NonConstantsUsedForDigitalReadFast( void ) __attribute__ (( error("Parameter for digitalReadFast() function is not constant") )); + +#if !defined(MEGATINYCORE) // megaTinyCore has it own digitalWriteFast function set, except digitalToggleFast(). + +//#define SANGUINO_PINOUT // define for Sanguino pinout + +// general macros/defines +#if !defined(BIT_READ) +# define BIT_READ(value, bit) ((value) & (1UL << (bit))) +#endif +#if !defined(BIT_SET) +# define BIT_SET(value, bit) ((value) |= (1UL << (bit))) +#endif +#if !defined(BIT_CLEAR) +# define BIT_CLEAR(value, bit) ((value) &= ~(1UL << (bit))) +#endif +#if !defined(BIT_WRITE) +# define BIT_WRITE(value, bit, bitvalue) (bitvalue ? BIT_SET(value, bit) : BIT_CLEAR(value, bit)) +#endif + +#include // declarations for the fallback to digitalWrite(), digitalRead() etc. + +// --- Arduino Mega and ATmega128x/256x based boards --- +#if (defined(ARDUINO_AVR_MEGA) || \ + defined(ARDUINO_AVR_MEGA1280) || \ + defined(ARDUINO_AVR_MEGA2560) || \ + defined(__AVR_ATmega1280__) || \ + defined(__AVR_ATmega1281__) || \ + defined(__AVR_ATmega2560__) || \ + defined(__AVR_ATmega2561__)) + +#define __digitalPinToPortReg(P) \ +(((P) >= 22 && (P) <= 29) ? &PORTA : \ +((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PORTB : \ +(((P) >= 30 && (P) <= 37) ? &PORTC : \ +((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PORTD : \ +((((P) <= 3) || (P) == 5) ? &PORTE : \ +(((P) >= 54 && (P) <= 61) ? &PORTF : \ +((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PORTG : \ +((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PORTH : \ +(((P) == 14 || (P) == 15) ? &PORTJ : \ +(((P) >= 62 && (P) <= 69) ? &PORTK : &PORTL)))))))))) + +#define __digitalPinToDDRReg(P) \ +(((P) >= 22 && (P) <= 29) ? &DDRA : \ +((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &DDRB : \ +(((P) >= 30 && (P) <= 37) ? &DDRC : \ +((((P) >= 18 && (P) <= 21) || (P) == 38) ? &DDRD : \ +((((P) <= 3) || (P) == 5) ? &DDRE : \ +(((P) >= 54 && (P) <= 61) ? &DDRF : \ +((((P) >= 39 && (P) <= 41) || (P) == 4) ? &DDRG : \ +((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &DDRH : \ +(((P) == 14 || (P) == 15) ? &DDRJ : \ +(((P) >= 62 && (P) <= 69) ? &DDRK : &DDRL)))))))))) + +#define __digitalPinToPINReg(P) \ +(((P) >= 22 && (P) <= 29) ? &PINA : \ +((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PINB : \ +(((P) >= 30 && (P) <= 37) ? &PINC : \ +((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PIND : \ +((((P) <= 3) || (P) == 5) ? &PINE : \ +(((P) >= 54 && (P) <= 61) ? &PINF : \ +((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PING : \ +((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PINH : \ +(((P) == 14 || (P) == 15) ? &PINJ : \ +(((P) >= 62 && (P) <= 69) ? &PINK : &PINL)))))))))) + +#define __digitalPinToBit(P) \ +(((P) >= 7 && (P) <= 9) ? (P) - 3 : \ +(((P) >= 10 && (P) <= 13) ? (P) - 6 : \ +(((P) >= 22 && (P) <= 29) ? (P) - 22 : \ +(((P) >= 30 && (P) <= 37) ? 37 - (P) : \ +(((P) >= 39 && (P) <= 41) ? 41 - (P) : \ +(((P) >= 42 && (P) <= 49) ? 49 - (P) : \ +(((P) >= 50 && (P) <= 53) ? 53 - (P) : \ +(((P) >= 54 && (P) <= 61) ? (P) - 54 : \ +(((P) >= 62 && (P) <= 69) ? (P) - 62 : \ +(((P) == 0 || (P) == 15 || (P) == 17 || (P) == 21) ? 0 : \ +(((P) == 1 || (P) == 14 || (P) == 16 || (P) == 20) ? 1 : \ +(((P) == 19) ? 2 : \ +(((P) == 5 || (P) == 6 || (P) == 18) ? 3 : \ +(((P) == 2) ? 4 : \ +(((P) == 3 || (P) == 4) ? 5 : 7))))))))))))))) + + +// --- Arduino MightyCore standard pinout --- +#elif (defined(__AVR_ATmega1284P__) || \ + defined(__AVR_ATmega1284__) || \ + defined(__AVR_ATmega644P__) || \ + defined(__AVR_ATmega644A__) || \ + defined(__AVR_ATmega644__) || \ + defined(__AVR_ATmega324PB__) || \ + defined(__AVR_ATmega324PA__) || \ + defined(__AVR_ATmega324P__) || \ + defined(__AVR_ATmega324A__) || \ + defined(__AVR_ATmega164P__) || \ + defined(__AVR_ATmega164A__) || \ + defined(__AVR_ATmega32__) || \ + defined(__AVR_ATmega16__) || \ + defined(__AVR_ATmega8535__)) && \ + !defined(BOBUINO_PINOUT) + +#if defined(__AVR_ATmega324PB__) +#define __digitalPinToPortReg(P) \ +(((P) <= 7) ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : (((P) >= 16 && (P) <= 23) ? &PORTC : (((P) >= 24 && (P) <= 31) ? &PORTA : &PORTE)))) +#define __digitalPinToDDRReg(P) \ +(((P) <= 7) ? &DDRB : (((P) >= 8 && (P) <= 15) ? &DDRD : (((P) >= 16 && (P) <= 23) ? &DDRC : (((P) >= 24 && (P) <= 31) ? &DDRA : &DDRE)))) +#define __digitalPinToPINReg(P) \ +(((P) <= 7) ? &PINB : (((P) >= 8 && (P) <= 15) ? &PIND : (((P) >= 16 && (P) <= 23) ? &PINC : (((P) >= 24 && (P) <= 31) ? &PINA : &PINE)))) +# if defined(SANGUINO_PINOUT) +#define __digitalPinToBit(P) \ +(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (((P) >= 16 && (P) <= 23) ? (7 - ((P) - 24)) : (P) - 32)))) +# else //MightyCore Pinout +#define __digitalPinToBit(P) \ +(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (((P) >= 16 && (P) <= 23) ? (P) - 24 : (P) - 32)))) +# endif +#elif defined(PORTA) +#define __digitalPinToPortReg(P) \ +(((P) <= 7) ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : (((P) >= 16 && (P) <= 23) ? &PORTC : &PORTA))) +#define __digitalPinToDDRReg(P) \ +(((P) <= 7) ? &DDRB : (((P) >= 8 && (P) <= 15) ? &DDRD : (((P) >= 16 && (P) <= 23) ? &DDRC : &DDRA))) +#define __digitalPinToPINReg(P) \ +(((P) <= 7) ? &PINB : (((P) >= 8 && (P) <= 15) ? &PIND : (((P) >= 16 && (P) <= 23) ? &PINC : &PINA))) +# if defined(SANGUINO_PINOUT) +#define __digitalPinToBit(P) \ +(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (7 - ((P) - 24))))) +# else //MightyCore Pinout +#define __digitalPinToBit(P) \ +(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (P) - 24))) +# endif +#else +#define __digitalPinToPortReg(P) \ +(((P) <= 7) ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : &PORTC)) +#define __digitalPinToDDRReg(P) \ +(((P) <= 7) ? &DDRB : (((P) >= 8 && (P) <= 15) ? &DDRD : &DDRC)) +#define __digitalPinToPINReg(P) \ +(((P) <= 7) ? &PINB : (((P) >= 8 && (P) <= 15) ? &PIND : &PINC)) +# if defined(SANGUINO_PINOUT) +#define __digitalPinToBit(P) \ +(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (7 - ((P) - 24))))) +# else //MightyCore Pinout +#define __digitalPinToBit(P) \ +(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (P) - 24))) +# endif +#endif + + +// --- Arduino Leonardo and ATmega16U4/32U4 based boards --- +#elif (defined(ARDUINO_AVR_LEONARDO) || \ + defined(__AVR_ATmega16U4__) || \ + defined(__AVR_ATmega32U4__)) +# if defined(TEENSYDUINO) + +#define __digitalPinToPortReg(P) \ +((((P) <= 4) || ((P) >= 13 && (P) <= 15)) ? &PORTB : (((P) == 9 || (P) == 10) ? &PORTC : (((P) >= 16 && (P) <= 21)) ? &PORTF : &PORTD)) +#define __digitalPinToDDRReg(P) \ +((((P) <= 4) || ((P) >= 13 && (P) <= 15)) ? &DDRB : (((P) == 9 || (P) == 10) ? &DDRC : (((P) >= 16 && (P) <= 21)) ? &DDRF : &DDRD)) +#define __digitalPinToPINReg(P) \ +((((P) <= 4) || ((P) >= 13 && (P) <= 15)) ? &PINB : (((P) == 9 || (P) == 10) ? &PINC : (((P) >= 16 && (P) <= 21)) ? &PINF : &PIND)) +#define __digitalPinToBit(P) \ +(((P) <= 3) ? (P) : \ +(((P) == 4 || (P) == 12) ? 7 : \ +(((P) <= 8) ? (P) - 5 : \ +(((P) <= 10) ? (P) - 3 : \ +(((P) == 11) ? 6 : \ +(((P) <= 15) ? (P) - 9 : \ +(((P) <= 19) ? 23 - (P) : \ +(((P) <= 21) ? 21 - (P) : (P) - 18)))))))) +# else + +#define __digitalPinToPortReg(P) \ +((((P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &PORTD : (((P) == 5 || (P) == 13) ? &PORTC : (((P) >= 18 && (P) <= 23)) ? &PORTF : (((P) == 7) ? &PORTE : &PORTB))) +#define __digitalPinToDDRReg(P) \ +((((P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &DDRD : (((P) == 5 || (P) == 13) ? &DDRC : (((P) >= 18 && (P) <= 23)) ? &DDRF : (((P) == 7) ? &DDRE : &DDRB))) +#define __digitalPinToPINReg(P) \ +((((P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &PIND : (((P) == 5 || (P) == 13) ? &PINC : (((P) >= 18 && (P) <= 23)) ? &PINF : (((P) == 7) ? &PINE : &PINB))) +#define __digitalPinToBit(P) \ +(((P) >= 8 && (P) <= 11) ? (P) - 4 : \ +(((P) >= 18 && (P) <= 21) ? 25 - (P) : \ +(((P) == 0) ? 2 : (((P) == 1) ? 3 : (((P) == 2) ? 1 : (((P) == 3) ? 0 : (((P) == 4) ? 4 : (((P) == 6) ? 7 : (((P) == 13) ? 7 : \ +(((P) == 14) ? 3 : (((P) == 15) ? 1 : (((P) == 16) ? 2 : (((P) == 17) ? 0 : (((P) == 22) ? 1 : (((P) == 23) ? 0 : \ +(((P) == 24) ? 4 : (((P) == 25) ? 7 : (((P) == 26) ? 4 : (((P) == 27) ? 5 : 6 ))))))))))))))))))) +# endif + +// --- Arduino Uno and ATmega168/328 based boards --- +#elif (defined(ARDUINO_AVR_UNO) || \ + defined(ARDUINO_AVR_DUEMILANOVE) || \ + defined(__AVR_ATmega8__) || \ + defined(__AVR_ATmega48__) || \ + defined(__AVR_ATmega48P__) || \ + defined(__AVR_ATmega48PB__) || \ + defined(__AVR_ATmega88P__) || \ + defined(__AVR_ATmega88PB__) || \ + defined(__AVR_ATmega168__) || \ + defined(__AVR_ATmega168PA__) || \ + defined(__AVR_ATmega168PB__) || \ + defined(__AVR_ATmega328__) || \ + defined(__AVR_ATmega328P__) || \ + defined(__AVR_ATmega328PB__)) + +#if defined(__AVR_ATmega48PB__) || defined(__AVR_ATmega88PB__) || defined(__AVR_ATmega168PB__) || defined(__AVR_ATmega328PB__) +#define __digitalPinToPortReg(P) \ +(((P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : (((P) >= 14 && (P) <= 19) ? &PORTC : &PORTE))) +#define __digitalPinToDDRReg(P) \ +(((P) <= 7) ? &DDRD : (((P) >= 8 && (P) <= 13) ? &DDRB : (((P) >= 14 && (P) <= 19) ? &DDRC : &DDRE))) +#define __digitalPinToPINReg(P) \ +(((P) <= 7) ? &PIND : (((P) >= 8 && (P) <= 13) ? &PINB : (((P) >= 14 && (P) <= 19) ? &PINC : &PINE))) +#define __digitalPinToBit(P) \ +(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (((P) >= 14 && (P) <= 19) ? (P) - 14 : (((P) >= 20 && (P) <= 21) ? (P) - 18 : (P) - 22)))) +#else +#define __digitalPinToPortReg(P) \ +(((P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : &PORTC)) +#define __digitalPinToDDRReg(P) \ +(((P) <= 7) ? &DDRD : (((P) >= 8 && (P) <= 13) ? &DDRB : &DDRC)) +#define __digitalPinToPINReg(P) \ +(((P) <= 7) ? &PIND : (((P) >= 8 && (P) <= 13) ? &PINB : &PINC)) +#define __digitalPinToBit(P) \ +(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (P) - 14)) +#endif + +// --- Arduino Uno WiFi Rev 2, Nano Every --- +#elif defined(__AVR_ATmega4809__) + +#define __digitalPinToPortReg(P) \ +(((P) == 2 || (P) == 7 ) ? &VPORTA.OUT : ((P) == 5 || (P) == 9 || (P) == 10) ? &VPORTB.OUT : ((P) == 4) ? &VPORTC.OUT : (((P) >= 14 && (P) <= 17) || (P) == 20 || (P) == 21) ? &VPORTD.OUT : ((P) == 8 || (P) == 11 || (P) == 12 || (P) == 13) ? &VPORTE.OUT : &VPORTF.OUT) +#define __digitalPinToDDRReg(P) \ +(((P) == 2 || (P) == 7 ) ? &VPORTA.DIR : ((P) == 5 || (P) == 9 || (P) == 10) ? &VPORTB.DIR : ((P) == 4) ? &VPORTC.DIR : (((P) >= 14 && (P) <= 17) || (P) == 20 || (P) == 21) ? &VPORTD.DIR : ((P) == 8 || (P) == 11 || (P) == 12 || (P) == 13) ? &VPORTE.DIR : &VPORTF.DIR) +#define __digitalPinToPINReg(P) \ +(((P) == 2 || (P) == 7 ) ? &VPORTA.IN : ((P) == 5 || (P) == 9 || (P) == 10) ? &VPORTB.IN : ((P) == 4) ? &VPORTC.IN : (((P) >= 14 && (P) <= 17) || (P) == 20 || (P) == 21) ? &VPORTD.IN : ((P) == 8 || (P) == 11 || (P) == 12 || (P) == 13) ? &VPORTE.IN : &VPORTF.IN) +#define __digitalPinToBit(P) \ +(((P) == 2 || (P) == 9 || (P) == 11 || (P) == 17) ? 0 : ((P) == 7 || (P) == 10 || (P) == 12 || (P) == 16) ? 1 : ((P) == 5 || (P) == 13 || (P) == 15 || (P) == 18) ? 2 : ((P) == 9 || (P) == 14 || (P) == 19) ? 3 : ((P) == 6 || (P) == 20) ? 4 : ((P) == 3 || (P) == 21) ? 5 : 6 ) + + +// TinyCore +// https://raw.githubusercontent.com/xukangmin/TinyCore/master/avr/package/package_tinycore_index.json +// https://docs.tinycore.dev/en/latest/ +#elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) +#define __digitalPinToPortReg(P) ((P) <= 5 ? &VPORTB.OUT : ((P) <= 9 ? &VPORTC.OUT : ((P) <= 16 ? &VPORTA.OUT : ((P) <= 18 ? &VPORTB.OUT : &VPORTC.OUT)))) +#define __digitalPinToDDRReg(P) ((P) <= 5 ? &VPORTB.DIR : ((P) <= 9 ? &VPORTC.DIR : ((P) <= 16 ? &VPORTA.DIR : ((P) <= 18 ? &VPORTB.DIR : &VPORTC.DIR)))) +#define __digitalPinToPINReg(P) ((P) <= 5 ? &VPORTB.IN : ((P) <= 9 ? &VPORTC.IN : ((P) <= 16 ? &VPORTA.IN : ((P) <= 18 ? &VPORTB.IN : &VPORTC.IN)))) +#define __digitalPinToBit(P) ( (P) <= 3 ? (3 - P) : ((P) <= 5 ? (P) : ((P) <= 9 ? (P - 6) : ((P) <= 16 ? ((P) - 9) : ((P) <= 18 ? ((P) - 11) : ((P) - 15))))) ) + +#elif defined(__AVR_ATtiny1614__) +#define __digitalPinToPortReg(P) ((P) <= 3 ? &VPORTA.OUT : ((P) <= 7 ? &VPORTB.OUT : &VPORTA.OUT)) +#define __digitalPinToDDRReg(P) ((P) <= 3 ? &VPORTA.DIR : ((P) <= 7 ? &VPORTB.DIR : &VPORTA.DIR)) +#define __digitalPinToPINReg(P) ((P) <= 3 ? &VPORTA.IN : ((P) <= 7 ? &VPORTB.IN : &VPORTA.IN)) +#define __digitalPinToBit(P) ( (P) <= 3 ? (P + 4) : ((P) <= 7 ? (7 - P) : ((P) <= 10 ? (P - 7) : (P) - 11)) ) + +#elif defined(__AVR_ATtiny816__) +// https://github.com/Arduino-IRremote/Arduino-IRremote/discussions/1029 +#define __digitalPinToPortReg(P) ((P) <= 3 ? &VPORTA.OUT : ((P) <= 9 ? &VPORTB.OUT : ((P) <= 13 ? &VPORTC.OUT : ((P) <= 17 ? &VPORTA.OUT : &VPORTC.OUT)))) +#define __digitalPinToDDRReg(P) ((P) <= 3 ? &VPORTA.DIR : ((P) <= 9 ? &VPORTB.DIR : ((P) <= 13 ? &VPORTC.DIR : ((P) <= 17 ? &VPORTA.DIR : &VPORTC.DIR)))) +#define __digitalPinToPINReg(P) ((P) <= 3 ? &VPORTA.IN : ((P) <= 9 ? &VPORTB.IN : ((P) <= 13 ? &VPORTC.IN : ((P) <= 17 ? &VPORTA.IN : &VPORTC.IN)))) +#define __digitalPinToBit(P) ( (P) <= 3 ? (P + 4) : ((P) <= 9 ? (9 - P) : ((P) <= 13 ? (P - 10) : ((P) <= 16 ? (P - 13) : ((P) - 17)))) ) + +// --- ATtinyX5 --- +#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) +// we have only PORTB +#define __digitalPinToPortReg(P) (&PORTB) +#define __digitalPinToDDRReg(P) (&DDRB) +#define __digitalPinToPINReg(P) (&PINB) +#define __digitalPinToBit(P) (((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (P) - 14)) + + +// --- ATtiny88 --- +#elif defined(__AVR_ATtiny88__) +# if defined(ARDUINO_AVR_DIGISPARKPRO) +#define __digitalPinToPortReg(P) ((P) <= 7 ? &PORTD : ((P) <= 14 ? &PORTB : ((P) <= 18 ? &PORTA : &PORTC))) +#define __digitalPinToDDRReg(P) ((P) <= 7 ? &DDRD : ((P) <= 14 ? &DDRB : ((P) <= 18 ? &DDRA : &DDRC))) +#define __digitalPinToPINReg(P) ((P) <= 7 ? &PIND : ((P) <= 14 ? &PINB : ((P) <= 18 ? &PINA : &PINC))) +#define __digitalPinToBit(P) ( (P) <= 7 ? (P) : ((P) <= 13 ? ((P) - 8) : ((P) == 14 ? 7 : ((P) <= 16 ? ((P) - 14) : ((P) <= 18 ? ((P) - 17) : ((P) == 25 ? 7 : ((P) - 19)))))) ) +# else +#define __digitalPinToPortReg(P) ((P) <= 7 ? &PORTD : ((P) <= 15 ? &PORTB : ((P) <= 22 ? &PORTC : ((P) <= 26 ? &PORTA : &PORTC)))) +#define __digitalPinToDDRReg(P) ((P) <= 7 ? &DDRD : ((P) <= 15 ? &DDRB : ((P) <= 22 ? &DDRC : ((P) <= 26 ? &DDRA : &DDRC)))) +#define __digitalPinToPINReg(P) ((P) <= 7 ? &PIND : ((P) <= 15 ? &PINB : ((P) <= 22 ? &PINC : ((P) <= 26 ? &PINA : &PINC)))) +#define __digitalPinToBit(P) ((P) <= 15 ? ((P) & 0x7) : ((P) == 16 ? (7) : ((P) <= 22 ? ((P) - 17) : ((P) == 27 ? (6) : ((P) - 23))))) +# endif + + +// --- ATtinyX4 + ATtinyX7 --- +#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \ + || defined(__AVR_ATtiny441__) || defined(__AVR_ATtiny841__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +# if defined(ARDUINO_AVR_DIGISPARKPRO) || PIN_PA7 == 5 +// Strange enumeration of pins on Digispark board and core library +#define __digitalPinToPortReg(P) (((P) <= 4) ? &PORTB : &PORTA) +#define __digitalPinToDDRReg(P) (((P) <= 4) ? &DDRB : &DDRA) +#define __digitalPinToPINReg(P) (((P) <= 4) ? &PINB : &PINA) +#define __digitalPinToBit(P) (((P) <= 2) ? (P) : (((P) == 3) ? 6 : (((P) == 4) ? 3 : (((P) == 5) ? 7 : (P) - 6 )))) +# else +// ATtinyX4: PORTA for 0 to 7, PORTB for 8 to 11 +// ATtinyX7: PORTA for 0 to 7, PORTB for 8 to 15 +#define __digitalPinToPortReg(P) (((P) <= 7) ? &PORTA : &PORTB) +#define __digitalPinToDDRReg(P) (((P) <= 7) ? &DDRA : &DDRB) +#define __digitalPinToPINReg(P) (((P) <= 7) ? &PINA : &PINB) +# endif +# if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny441__) || defined(__AVR_ATtiny841__) +// https://github.com/SpenceKonde/ATTinyCore/blob/v2.0.0-devThis-is-the-head-submit-PRs-against-this/avr/variants/tinyx41_cw/pins_arduino.h#L334 +// Clockwise layout +#define __digitalPinToBit(P) (((P) <= 7) ? (P) : ((P) == 11 ? (3) : 10 - (P))) +# else +#define __digitalPinToBit(P) (((P) <= 7) ? (P) : (P) - 8 ) +# endif + +#endif + +#if !defined(digitalWriteFast) +# if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && defined(__digitalPinToPortReg) +# if defined(THROW_ERROR_IF_NOT_FAST) +#define digitalWriteFast(P, V) \ +do { \ + if (__builtin_constant_p(P)) { \ + BIT_WRITE(*__digitalPinToPortReg(P), __digitalPinToBit(P), (V)); \ + } else { \ + NonConstantsUsedForDigitalWriteFast(); \ + } \ +} while (0) +# else +#define digitalWriteFast(P, V) \ +do { \ + if (__builtin_constant_p(P)) { \ + BIT_WRITE(*__digitalPinToPortReg(P), __digitalPinToBit(P), (V)); \ + } else { \ + digitalWrite((P), (V)); \ + } \ +} while (0) +# endif // defined(THROW_ERROR_IF_NOT_FAST) +# else +#define digitalWriteFast digitalWrite +# endif +#endif + +#if !defined(pinModeFast) +# if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && defined(__digitalPinToPortReg) +# if defined(THROW_ERROR_IF_NOT_FAST) +#define pinModeFast(P, V) \ +do { \ + if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \ + if (V == INPUT_PULLUP) {\ + BIT_CLEAR(*__digitalPinToDDRReg(P), __digitalPinToBit(P)); \ + BIT_SET(*__digitalPinToPortReg(P), __digitalPinToBit(P)); \ + } else { \ + BIT_WRITE(*__digitalPinToDDRReg(P), __digitalPinToBit(P), (V)); \ + } \ + } else { \ + NonConstantsUsedForPinModeFast(); \ + } \ +} while (0) +# else +#define pinModeFast(P, V) \ +do { \ + if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \ + if (V == INPUT_PULLUP) {\ + BIT_CLEAR(*__digitalPinToDDRReg(P), __digitalPinToBit(P)); \ + BIT_SET(*__digitalPinToPortReg(P), __digitalPinToBit(P)); \ + } else { \ + BIT_WRITE(*__digitalPinToDDRReg(P), __digitalPinToBit(P), (V)); \ + } \ + } else { \ + pinMode((P), (V)); \ + } \ +} while (0) +# endif // defined(THROW_ERROR_IF_NOT_FAST) +# else +#define pinModeFast pinMode +# endif +#endif // !defined(pinModeFast) + +#if !defined(digitalReadFast) +# if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && defined(__digitalPinToPINReg) +# if defined(THROW_ERROR_IF_NOT_FAST) +#define digitalReadFast(P) ( (int) __digitalReadFast((P)) ) +// since we have return values, it is easier to implement it by ?: +#define __digitalReadFast(P ) ( (__builtin_constant_p(P) ) ? (( BIT_READ(*__digitalPinToPINReg(P), __digitalPinToBit(P))) ? HIGH:LOW ) : NonConstantsUsedForDigitalReadFast() ) +# else +#define digitalReadFast(P) ( (int) __digitalReadFast((P)) ) +// since we have return values, it is easier to implement it by ?: +#define __digitalReadFast(P ) ( (__builtin_constant_p(P) ) ? (( BIT_READ(*__digitalPinToPINReg(P), __digitalPinToBit(P))) ? HIGH:LOW ) : digitalRead((P)) ) +# endif // defined(THROW_ERROR_IF_NOT_FAST) +# else +#define digitalReadFast digitalRead +# endif +#endif // !defined(digitalReadFast) + +#if !defined(digitalToggleFast) +# if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && defined(__digitalPinToPINReg) +# if defined(THROW_ERROR_IF_NOT_FAST) +#define digitalToggleFast(P) \ +do { \ + if (__builtin_constant_p(P)) { \ + BIT_SET(*__digitalPinToPINReg(P), __digitalPinToBit(P)); \ + } else { \ + NonConstantsUsedForDigitalToggleFast(); \ + } \ +} while (0) +# else +#define digitalToggleFast(P) \ +do { \ + if (__builtin_constant_p(P)) { \ + BIT_SET(*__digitalPinToPINReg(P), __digitalPinToBit(P)); \ + } else { \ + digitalWrite(P, ! digitalRead(P)); \ + } \ +} while (0) +# endif // defined(THROW_ERROR_IF_NOT_FAST) +# else +#define digitalToggleFast(P) digitalWrite(P, ! digitalRead(P)) +# endif +#endif // !defined(digitalToggleFast) + +#endif // !defined(MEGATINYCORE) +#endif //__digitalWriteFast_h_ diff --git a/src/esp32.cpp b/src/esp32.cpp deleted file mode 100644 index c5b7e1193..000000000 --- a/src/esp32.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifdef ESP32 - -// This file contains functions specific to the ESP32. - -#include "IRremote.h" - -// "Idiot check" -#ifdef USE_DEFAULT_ENABLE_IR_IN -#error Must undef USE_DEFAULT_ENABLE_IR_IN -#endif - -hw_timer_t *timer; -IRAM_ATTR void IRTimer(); // defined in IRremote.cpp, masqueraded as ISR(TIMER_INTR_NAME) - -//+============================================================================= -// initialization -// -void IRrecv::enableIRIn() { -// Interrupt Service Routine - Fires every 50uS - // ESP32 has a proper API to setup timers, no weird chip macros needed - // simply call the readable API versions :) - // 3 timers, choose #1, 80 divider nanosecond precision, 1 to count up - timer = timerBegin(1, 80, 1); - timerAttachInterrupt(timer, &IRTimer, 1); - // every 50ns, autoreload = true - timerAlarmWrite(timer, 50, true); - timerAlarmEnable(timer); - - // Initialize state machine variables - irparams.rcvstate = IR_REC_STATE_IDLE; - irparams.rawlen = 0; - - // Set pin modes - pinMode(irparams.recvpin, INPUT); -} - -void IRrecv::disableIRIn() { - timerEnd(timer); - timerDetachInterrupt(timer); -} - -void IRsend::enableIROut(int khz) { - ledcSetup(LED_CHANNEL, khz * 1000, 8); // 8 bit PWM resolution - ledcAttachPin(IR_SEND_PIN, LED_CHANNEL); // bind pin to channel -} - -#endif // ESP32 diff --git a/src/irPronto.cpp b/src/irPronto.cpp deleted file mode 100644 index acdea261c..000000000 --- a/src/irPronto.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/** - * @file irPronto.cpp - * @brief In this file, the functions IRrecv::dumpPronto and - * IRsend::sendPronto are defined. - */ - -#include "IRremote.h" - -// DO NOT EXPORT from this file -static const uint16_t MICROSECONDS_T_MAX = 0xFFFFU; -static const uint16_t learnedToken = 0x0000U; -static const uint16_t learnedNonModulatedToken = 0x0100U; -static const unsigned int bitsInHexadecimal = 4U; -static const unsigned int digitsInProntoNumber = 4U; -static const unsigned int numbersInPreamble = 4U; -static const unsigned int hexMask = 0xFU; -static const uint32_t referenceFrequency = 4145146UL; -static const uint16_t fallbackFrequency = 64767U; // To use with frequency = 0; -static const uint32_t microsecondsInSeconds = 1000000UL; -static const unsigned int RESULT_JUNK_COUNT = 1U; - -static unsigned int toFrequencyKHz(uint16_t code) { - return ((referenceFrequency / code) + 500) / 1000; -} - -void IRsend::sendPronto(const uint16_t *data, unsigned int size, unsigned int times) { - unsigned int timebase = (microsecondsInSeconds * data[1] + referenceFrequency / 2) / referenceFrequency; - unsigned int khz; - switch (data[0]) { - case learnedToken: // normal, "learned" - khz = toFrequencyKHz(data[1]); - break; - case learnedNonModulatedToken: // non-demodulated, "learned" - khz = 0U; - break; - default: - return; // There are other types, but they are not handled yet. - } - unsigned int intros = 2 * data[2]; - unsigned int repeats = 2 * data[3]; - if (numbersInPreamble + intros + repeats != size) // inconsistent sizes - return; - - unsigned int durations[intros + repeats]; - for (unsigned int i = 0; i < intros + repeats; i++) { - uint32_t duration = ((uint32_t) data[i + numbersInPreamble]) * timebase; - durations[i] = (unsigned int) ((duration <= MICROSECONDS_T_MAX) ? duration : MICROSECONDS_T_MAX); - } - - unsigned int numberRepeats = intros > 0 ? times - 1 : times; - if (intros > 0) { - sendRaw(durations, intros - 1, khz); - } - - if (numberRepeats == 0) - return; - - delay(durations[intros - 1] / 1000U); - for (unsigned int i = 0; i < numberRepeats; i++) { - sendRaw(durations + intros, repeats - 1, khz); - if (i < numberRepeats - 1) { // skip last wait - delay(durations[intros + repeats - 1] / 1000U); - } - } -} - -void IRsend::sendPronto(const char *str, unsigned int times) { - size_t len = strlen(str) / (digitsInProntoNumber + 1) + 1; - uint16_t data[len]; - const char *p = str; - char *endptr[1]; - for (unsigned int i = 0; i < len; i++) { - long x = strtol(p, endptr, 16); - if (x == 0 && i >= numbersInPreamble) { - // Alignment error?, bail immediately (often right result). - len = i; - break; - } - data[i] = static_cast(x); // If input is conforming, there can be no overflow! - p = *endptr; - } - sendPronto(data, len, times); -} - -#if HAS_FLASH_READ -void IRsend::sendPronto_PF(uint_farptr_t str, unsigned int times) { - size_t len = strlen_PF(STRCPY_PF_CAST(str)); - char work[len + 1]; - strncpy_PF(work, STRCPY_PF_CAST(str), len); - sendPronto(work, times); -} - -void IRsend::sendPronto_PF(const char *str, unsigned int times) { - sendPronto_PF(reinterpret_cast(str), times); // to avoid infinite recursion -} -; - -void IRsend::sendPronto(const __FlashStringHelper *str, unsigned int times) { - return sendPronto_PF(reinterpret_cast(str), times); -} -#endif - -static uint16_t effectiveFrequency(uint16_t frequency) { - return frequency > 0 ? frequency : fallbackFrequency; -} - -static uint16_t toTimebase(uint16_t frequency) { - return microsecondsInSeconds / effectiveFrequency(frequency); -} - -static uint16_t toFrequencyCode(uint16_t frequency) { - return referenceFrequency / effectiveFrequency(frequency); -} - -static char hexDigit(unsigned int x) { - return (char) (x <= 9 ? ('0' + x) : ('A' + (x - 10))); -} - -static void dumpDigit(Print *aSerial, unsigned int number) { - aSerial->print(hexDigit(number)); -} - -static void dumpNumber(Print *aSerial, uint16_t number) { - for (unsigned int i = 0; i < digitsInProntoNumber; i++) { - unsigned int shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i); - dumpDigit(aSerial, (number >> shifts) & hexMask); - } - aSerial->print(' '); -} - -static void dumpDuration(Print *aSerial, uint16_t duration, uint16_t timebase) { - dumpNumber(aSerial, (duration * MICROS_PER_TICK + timebase / 2) / timebase); -} - -static void dumpSequence(Print *aSerial, const volatile uint16_t *data, size_t length, uint16_t timebase) { - for (unsigned int i = 0; i < length; i++) - dumpDuration(aSerial, data[i], timebase); - - dumpDuration(aSerial, _GAP, timebase); -} - -/* - * Using Print instead of Stream saves 1020 bytes program memory - * Changed from & to * parameter type to be more transparent and consistent with other code of IRremote - */ -void IRrecv::dumpPronto(Print *aSerial, unsigned int frequency) { - dumpNumber(aSerial, frequency > 0 ? learnedToken : learnedNonModulatedToken); - dumpNumber(aSerial, toFrequencyCode(frequency)); - dumpNumber(aSerial, (results.rawlen + 1) / 2); - dumpNumber(aSerial, 0); - unsigned int timebase = toTimebase(frequency); - dumpSequence(aSerial, results.rawbuf + RESULT_JUNK_COUNT, results.rawlen - RESULT_JUNK_COUNT, timebase); -} - -//+============================================================================= -// Dump out the raw data as Pronto Hex. -// I know Stream * is locally inconsistent, but all global print functions use it -// -void IRrecv::printIRResultAsPronto(Print *aSerial, unsigned int frequency) { - aSerial->print("Pronto Hex: "); - dumpPronto(aSerial, frequency); - aSerial->println(); -} diff --git a/src/irReceive.cpp b/src/irReceive.cpp deleted file mode 100644 index 61c0de0e3..000000000 --- a/src/irReceive.cpp +++ /dev/null @@ -1,752 +0,0 @@ -#include "IRremote.h" - -//+============================================================================= -// Decodes the received IR message -// Returns 0 if no data ready, 1 if data ready. -// Results of decoding are stored in results -// -bool IRrecv::decode() { - if (irparams.rcvstate != IR_REC_STATE_STOP) { - return false; - } - - /* - * First copy 3 values from irparams to internal results structure - */ - results.rawbuf = irparams.rawbuf; - results.rawlen = irparams.rawlen; - results.overflow = irparams.overflow; - - // reset optional values - results.address = 0; - results.isRepeat = false; - -#if DECODE_NEC_STANDARD - DBG_PRINTLN("Attempting NEC_STANDARD decode"); - if (decodeNECStandard()) { - return true; - } -#endif - -#if DECODE_NEC - DBG_PRINTLN("Attempting NEC decode"); - if (decodeNEC()) { - return true; - } -#endif - -#if DECODE_SHARP - DBG_PRINTLN("Attempting Sharp decode"); - if (decodeSharp()) { - return true; - } -#endif - -#if DECODE_SHARP_ALT - DBG_PRINTLN("Attempting SharpAlt decode"); - if (decodeSharpAlt()) { - return true; - } -#endif - -#if DECODE_SONY - DBG_PRINTLN("Attempting Sony decode"); - if (decodeSony()) { - return true; - } -#endif - -#if DECODE_SANYO - DBG_PRINTLN("Attempting Sanyo decode"); - if (decodeSanyo()) { - return true; - } -#endif - -//#if DECODE_LEGO_PF -// DBG_PRINTLN("Attempting Mitsubishi decode"); -// if (decodeMitsubishi()) { -// return true; -// } -//#endif - -#if DECODE_RC5 - DBG_PRINTLN("Attempting RC5 decode"); - if (decodeRC5()) { - return true; - } -#endif - -#if DECODE_RC6 - DBG_PRINTLN("Attempting RC6 decode"); - if (decodeRC6()) { - return true; - } -#endif - -#if DECODE_PANASONIC - DBG_PRINTLN("Attempting Panasonic decode"); - if (decodePanasonic()) { - return true; - } -#endif - -#if DECODE_LG - DBG_PRINTLN("Attempting LG decode"); - if (decodeLG()) { - return true; - } -#endif - -#if DECODE_JVC - DBG_PRINTLN("Attempting JVC decode"); - if (decodeJVC()) { - return true; - } -#endif - -#if DECODE_SAMSUNG - DBG_PRINTLN("Attempting SAMSUNG decode"); - if (decodeSAMSUNG()) { - return true; - } -#endif - -#if DECODE_WHYNTER - DBG_PRINTLN("Attempting Whynter decode"); - if (decodeWhynter()) { - return true; - } -#endif - -//#if DECODE_AIWA_RC_T501 -// DBG_PRINTLN("Attempting Aiwa RC-T501 decode"); -// if (decodeAiwaRCT501()) { -// return true; -// } -//#endif - -#if DECODE_DENON - DBG_PRINTLN("Attempting Denon decode"); - if (decodeDenon()) { - return true; - } -#endif - -#if DECODE_LEGO_PF - DBG_PRINTLN("Attempting Lego Power Functions"); - if (decodeLegoPowerFunctions()) { - return true; - } -#endif - -#if DECODE_MAGIQUEST - DBG_PRINTLN("Attempting MagiQuest decode"); - if (decodeMagiQuest()) { - return true; - } -#endif - -#if DECODE_HASH - DBG_PRINTLN("Hash decode"); - // decodeHash returns a hash on any input. - // Thus, it needs to be last in the list. - // If you add any decodes, add them before this. - if (decodeHash()) { - return true; - } -#endif - - // Throw away and start over - resume(); - return false; -} - -//+============================================================================= -IRrecv::IRrecv(int recvpin) { - irparams.recvpin = recvpin; - irparams.blinkflag = 0; -} - -IRrecv::IRrecv(int recvpin, int blinkpin) { - irparams.recvpin = recvpin; - irparams.blinkpin = blinkpin; - pinMode(blinkpin, OUTPUT); - irparams.blinkflag = 0; -} - -//+============================================================================= -// initialization -// -#ifdef USE_DEFAULT_ENABLE_IR_IN -void IRrecv::enableIRIn() { -// the interrupt Service Routine fires every 50 uS - noInterrupts(); - // Setup pulse clock timer interrupt - // Prescale /8 (16M/8 = 0.5 microseconds per tick) - // Therefore, the timer interval can range from 0.5 to 128 microseconds - // Depending on the reset value (255 to 0) - timerConfigForReceive(); - - // Timer2 Overflow Interrupt Enable - TIMER_ENABLE_RECEIVE_INTR; - - TIMER_RESET_INTR_PENDING; - - interrupts(); - - // Initialize state machine state - irparams.rcvstate = IR_REC_STATE_IDLE; - // irparams.rawlen = 0; // not required - - // Set pin modes - pinMode(irparams.recvpin, INPUT); -} - -void IRrecv::disableIRIn() { - TIMER_DISABLE_RECEIVE_INTR; -} - -#endif // USE_DEFAULT_ENABLE_IR_IN - -//+============================================================================= -// Enable/disable blinking of pin 13 on IR processing -// -void IRrecv::blink13(int blinkflag) { -#ifdef BLINKLED - irparams.blinkflag = blinkflag; - if (blinkflag) { - pinMode(BLINKLED, OUTPUT); - } -#endif -} - -//+============================================================================= -// Return if receiving new IR signals -// -bool IRrecv::isIdle() { - return (irparams.rcvstate == IR_REC_STATE_IDLE || irparams.rcvstate == IR_REC_STATE_STOP) ? true : false; -} - -bool IRrecv::available() { - if (irparams.rcvstate != IR_REC_STATE_STOP) { - return false; - } - results.rawbuf = irparams.rawbuf; - results.rawlen = irparams.rawlen; - - results.overflow = irparams.overflow; - if (!results.overflow) { - return true; - } - resume(); //skip overflowed buffer - return false; -} - -//+============================================================================= -// Restart the ISR state machine -// -void IRrecv::resume() { - irparams.rcvstate = IR_REC_STATE_IDLE; -// irparams.rawlen = 0; // not required -} - -# if DECODE_HASH -//+============================================================================= -// hashdecode - decode an arbitrary IR code. -// Instead of decoding using a standard encoding scheme -// (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. -// -// The algorithm: look at the sequence of MARK signals, and see if each one -// is shorter (0), the same length (1), or longer (2) than the previous. -// Do the same with the SPACE signals. Hash the resulting sequence of 0's, -// 1's, and 2's to a 32-bit value. This will give a unique value for each -// different code (probably), for most code systems. -// -// http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html -// -// Compare two tick values, returning 0 if newval is shorter, -// 1 if newval is equal, and 2 if newval is longer -// Use a tolerance of 20% -// -int IRrecv::compare(unsigned int oldval, unsigned int newval) { - if (newval * 10 < oldval * 8) { - return 0; - } - if (oldval * 10 < newval * 8) { - return 2; - } - return 1; -} - -/* - * Decode pulse distance protocols. - * The mark (pulse) has constant length, the length of the space determines the bit value. - * Each bit looks like: MARK + SPACE_1 -> 1 - * or : MARK + SPACE_0 -> 0 - * Data is read MSB first if not otherwise enabled. - * Input is results.rawbuf - * Output is results.value - */ -bool IRrecv::decodePulseDistanceData(uint8_t aNumberOfBits, uint8_t aStartOffset, unsigned int aBitMarkMicros, - unsigned int aOneSpaceMicros, unsigned int aZeroSpaceMicros, bool aMSBfirst) { - unsigned long tDecodedData = 0; - - if (aMSBfirst) { - for (uint8_t i = 0; i < aNumberOfBits; i++) { - // Check for constant length mark - if (!MATCH_MARK(results.rawbuf[aStartOffset], aBitMarkMicros)) { - return false; - } - aStartOffset++; - - // Check for variable length space indicating a 0 or 1 - if (MATCH_SPACE(results.rawbuf[aStartOffset], aOneSpaceMicros)) { - tDecodedData = (tDecodedData << 1) | 1; - } else if (MATCH_SPACE(results.rawbuf[aStartOffset], aZeroSpaceMicros)) { - tDecodedData = (tDecodedData << 1) | 0; - } else { - return false; - } - aStartOffset++; - } - } -#if defined(LSB_FIRST_REQUIRED) - else { - for (unsigned long mask = 1UL; aNumberOfBits > 0; mask <<= 1, aNumberOfBits--) { - // Check for constant length mark - if (!MATCH_MARK(results.rawbuf[aStartOffset], aBitMarkMicros)) { - return false; - } - aStartOffset++; - - // Check for variable length space indicating a 0 or 1 - if (MATCH_SPACE(results.rawbuf[aStartOffset], aOneSpaceMicros)) { - tDecodedData |= mask; // set the bit - } else if (MATCH_SPACE(results.rawbuf[aStartOffset], aZeroSpaceMicros)) { - // do not set the bit - } else { - return false; - } - - aStartOffset++; - } - } -#endif - results.value = tDecodedData; - return true; -} - -//+============================================================================= -// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param -// Converts the raw code values into a 32-bit hash code. -// Hopefully this code is unique for each button. -// This isn't a "real" decoding, just an arbitrary value. -// -#define FNV_PRIME_32 16777619 -#define FNV_BASIS_32 2166136261 - -bool IRrecv::decodeHash() { - long hash = FNV_BASIS_32; - -// Require at least 6 samples to prevent triggering on noise - if (results.rawlen < 6) { - return false; - } - - for (unsigned int i = 1; (i + 2) < results.rawlen; i++) { - int value = compare(results.rawbuf[i], results.rawbuf[i + 2]); - // Add value into the hash - hash = (hash * FNV_PRIME_32) ^ value; - } - - results.value = hash; - results.bits = 32; - results.decode_type = UNKNOWN; - - return true; -} -bool IRrecv::decodeHash(decode_results *aResults) { - bool aReturnValue = decodeHash(); - *aResults = results; - return aReturnValue; -} -#endif // defined(DECODE_HASH) - -const char* IRrecv::getProtocolString() { - switch (results.decode_type) { - default: - case UNKNOWN: - return ("UNKNOWN"); - break; -//#if DECODE_AIWA_RC_T501 -// case AIWA_RC_T501: -// return ("AIWA_RC_T501"); -// break; -//#endif -#if DECODE_BOSEWAVE - case BOSEWAVE: - return ("BOSEWAVE"); - break; -#endif -#if DECODE_DENON - case DENON: - return ("Denon"); - break; -#endif -#if DECODE_DISH - case DISH: - return("DISH"); - break; -#endif -#if DECODE_JVC - case JVC: - return ("JVC"); - break; -#endif -#if DECODE_LEGO_PF - case LG: - return("LEGO"); - break; -#endif -#if DECODE_LG - case LEGO_PF: - return ("LEGO_PF"); - break; -#endif -#if DECODE_MAGIQUEST - case MAGIQUEST: - return ("MAGIQUEST"); - break; -#endif -//#if DECODE_MITSUBISHI -// case MITSUBISHI: -// return ("MITSUBISHI"); -// break; -//#endif -#if DECODE_NEC_STANDARD - case NEC_STANDARD: - return ("NEC_STANDARD"); - break; -#endif -#if DECODE_NEC - case NEC: - return("NEC"); - break; -#endif -#if DECODE_PANASONIC - case PANASONIC: - return ("PANASONIC"); - break; -#endif -#if DECODE_RC5 - case RC5: - return ("RC5"); - break; -#endif -#if DECODE_RC6 - case RC6: - return ("RC6"); - break; -#endif -#if DECODE_SAMSUNG - case SAMSUNG: - return ("SAMSUNG"); - break; -#endif -#if DECODE_SANYO - case SANYO: - return ("SANYO"); - break; -#endif -#if DECODE_SHARP - case SHARP: - return ("SHARP"); - break; -#endif -#if DECODE_SHARP_ALT - case SHARP_ALT: - return ("SHARP_ALT"); - break; -#endif -#if DECODE_SONY - case SONY: - return ("SONY"); - break; -#endif -#if DECODE_WHYNTER - case WHYNTER: - return ("WHYNTER"); - break; -#endif - } -} - -void IRrecv::printResultShort(Print *aSerial) { - aSerial->print(F("Protocol=")); - aSerial->print(getProtocolString()); - aSerial->print(F(" Data=0x")); - aSerial->print(results.value, HEX); - if (results.isRepeat) { - aSerial->print(F(" R")); - } - if (results.address != 0) { - aSerial->print(F(" Address=0x")); - aSerial->print(results.address, HEX); - } -} - -void IRrecv::printIRResultRaw(Print *aSerial) { - // Dumps out the decode_results structure. - // Call this after IRrecv::decode() - int count = results.rawlen; - printResultShort(&Serial); - - aSerial->print(" ("); - aSerial->print(results.bits, DEC); - aSerial->println(" bits)"); - aSerial->print("rawData["); - aSerial->print(count, DEC); - aSerial->print("]: "); - - for (int i = 0; i < count; i++) { - if (i & 1) { - aSerial->print(results.rawbuf[i] * MICROS_PER_TICK, DEC); - } else { - aSerial->write('-'); - aSerial->print((unsigned long) results.rawbuf[i] * MICROS_PER_TICK, DEC); - } - aSerial->print(" "); - } - aSerial->println(); -} - -//+============================================================================= -// Dump out the decode_results structure. -// -void IRrecv::printIRResultRawFormatted(Print *aSerial) { - // Print Raw data - aSerial->print("rawData["); - aSerial->print(results.rawlen - 1, DEC); - aSerial->println("]: "); - - for (unsigned int i = 1; i < results.rawlen; i++) { - unsigned long x = results.rawbuf[i] * MICROS_PER_TICK; - if (!(i & 1)) { // even - aSerial->print("-"); - if (x < 1000) { - aSerial->print(" "); - } - if (x < 100) { - aSerial->print(" "); - } - aSerial->print(x, DEC); - } else { // odd - aSerial->print(" "); - aSerial->print("+"); - if (x < 1000) { - aSerial->print(" "); - } - if (x < 100) { - aSerial->print(" "); - } - aSerial->print(x, DEC); - if (i < results.rawlen - 1) { - aSerial->print(", "); //',' not needed for last one - } - } - if (!(i % 8)) { - aSerial->println(""); - } - } - aSerial->println(""); // Newline -} -//+============================================================================= -// Dump out the decode_results structure. -// -void IRrecv::printIRResultAsCArray(Print *aSerial) { - // Start declaration - aSerial->print("uint16_t "); // variable type - aSerial->print("rawData["); // array name - aSerial->print(results.rawlen - 1, DEC); // array size - aSerial->print("] = {"); // Start declaration - - // Dump data - for (unsigned int i = 1; i < results.rawlen; i++) { - aSerial->print(results.rawbuf[i] * MICROS_PER_TICK, DEC); - if (i < results.rawlen - 1) - aSerial->print(","); // ',' not needed on last one - if (!(i & 1)) - aSerial->print(" "); - } - - // End declaration - aSerial->print("};"); // - - // Comment - aSerial->print(" // "); - printResultShort(aSerial); - - // Newline - aSerial->println(""); -} - -void IRrecv::printIRResultAsCVariables(Print *aSerial) { - // Now dump "known" codes - if (results.decode_type != UNKNOWN) { - - // Some protocols have an address - if(results.address != 0){ - aSerial->print("uint16_t address = 0x"); - aSerial->print(results.address, HEX); - aSerial->println(";"); - } - - // All protocols have data - aSerial->print("uint16_t data = 0x"); - aSerial->print(results.value, HEX); - aSerial->println(";"); - aSerial->println(); - } -} - -/* - * DEPRECATED - * With parameter aResults for backwards compatibility - * Contains no new (since 5/2020) protocols. - */ -bool IRrecv::decode(decode_results *aResults) { - if (irparams.rcvstate != IR_REC_STATE_STOP) { - return false; - } - - /* - * First copy 3 values from irparams to internal results structure - */ - results.rawbuf = irparams.rawbuf; - results.rawlen = irparams.rawlen; - results.overflow = irparams.overflow; - - // reset optional values - results.address = 0; - results.isRepeat = false; - -#if DECODE_NEC - DBG_PRINTLN("Attempting NEC decode"); - if (decodeNEC(aResults)) { - return true; - } -#endif - -#if DECODE_SHARP - DBG_PRINTLN("Attempting Sharp decode"); - if (decodeSharp(aResults)) { - return true; - } -#endif - -#if DECODE_SHARP_ALT - DBG_PRINTLN("Attempting SharpAlt decode"); - if (decodeSharpAlt(aResults)) { - return true; - } -#endif - -#if DECODE_SONY - DBG_PRINTLN("Attempting Sony decode"); - if (decodeSony(aResults)) { - return true; - } -#endif - -#if DECODE_SANYO - DBG_PRINTLN("Attempting Sanyo decode"); - if (decodeSanyo(aResults)) { - return true; - } -#endif - -//#if DECODE_MITSUBISHI -// DBG_PRINTLN("Attempting Mitsubishi decode"); -// if (decodeMitsubishi(aResults)) { -// return true; -// } -//#endif - -#if DECODE_RC5 - DBG_PRINTLN("Attempting RC5 decode"); - if (decodeRC5(aResults)) { - return true; - } -#endif - -#if DECODE_RC6 - DBG_PRINTLN("Attempting RC6 decode"); - if (decodeRC6(aResults)) { - return true; - } -#endif - -#if DECODE_PANASONIC - DBG_PRINTLN("Attempting Panasonic decode"); - if (decodePanasonic(aResults)) { - return true; - } -#endif - -#if DECODE_LG - DBG_PRINTLN("Attempting LG decode"); - if (decodeLG(aResults)) { - return true; - } -#endif - -#if DECODE_JVC - DBG_PRINTLN("Attempting JVC decode"); - if (decodeJVC(aResults)) { - return true; - } -#endif - -#if DECODE_SAMSUNG - DBG_PRINTLN("Attempting SAMSUNG decode"); - if (decodeSAMSUNG(aResults)) { - return true; - } -#endif - -#if DECODE_WHYNTER - DBG_PRINTLN("Attempting Whynter decode"); - if (decodeWhynter(aResults)) { - return true; - } -#endif - -//#if DECODE_AIWA_RC_T501 -// DBG_PRINTLN("Attempting Aiwa RC-T501 decode"); -// if (decodeAiwaRCT501(aResults)) { -// return true; -// } -//#endif - -#if DECODE_DENON - DBG_PRINTLN("Attempting Denon decode"); - if (decodeDenon(aResults)) { - return true; - } -#endif - -#if defined(DECODE_HASH) - DBG_PRINTLN("Hash decode"); - // decodeHash returns a hash on any input. - // Thus, it needs to be last in the list. - // If you add any decodes, add them before this. - if (decodeHash(aResults)) { - return true; - } -#endif - - // Throw away and start over - resume(); - return false; -} diff --git a/src/irSend.cpp b/src/irSend.cpp deleted file mode 100644 index 4e648245e..000000000 --- a/src/irSend.cpp +++ /dev/null @@ -1,230 +0,0 @@ -#include "IRremote.h" - -#ifdef SENDING_SUPPORTED // from IRremoteBoardDefs.h -//+============================================================================= -void IRsend::sendRaw(const unsigned int buf[], unsigned int len, unsigned int hz) { - // Set IR carrier frequency - enableIROut(hz); - - for (unsigned int i = 0; i < len; i++) { - if (i & 1) { - space(buf[i]); - } else { - mark(buf[i]); - } - } - - space(0); // Always end with the LED off -} - -void IRsend::sendRaw_P(const unsigned int buf[], unsigned int len, unsigned int hz) { -#if !defined(__AVR__) - sendRaw(buf,len,hz); // Let the function work for non AVR platforms -#else - // Set IR carrier frequency - enableIROut(hz); - - for (unsigned int i = 0; i < len; i++) { - uint16_t duration = pgm_read_word_near(buf + sizeof(uint16_t) * i); - if (i & 1) { - space(duration); - } else { - mark(duration); - } - } - space(0); // Always end with the LED off -#endif - -} - -#ifdef USE_SOFT_SEND_PWM -void inline IRsend::sleepMicros(unsigned long us) { -#ifdef USE_SPIN_WAIT - sleepUntilMicros(micros() + us); -#else - if (us > 0U) { // Is this necessary? (Official docu https://www.arduino.cc/en/Reference/DelayMicroseconds does not tell.) - delayMicroseconds((unsigned int) us); - } -#endif -} - -void inline IRsend::sleepUntilMicros(unsigned long targetTime) { -#ifdef USE_SPIN_WAIT - while (micros() < targetTime) - ; -#else - unsigned long now = micros(); - if (now < targetTime) { - sleepMicros(targetTime - now); - } -#endif -} -#endif // USE_SOFT_SEND_PWM - -//+============================================================================= -// Sends PulseDistance data from MSB to LSB -// -void IRsend::sendPulseDistanceWidthData(unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros, - unsigned int aZeroSpaceMicros, unsigned long aData, uint8_t aNumberOfBits, bool aMSBfirst) { - - if (aMSBfirst) { // Send the MSB first. - // send data from MSB to LSB until mask bit is shifted out - for (unsigned long mask = 1UL << (aNumberOfBits - 1); mask; mask >>= 1) { - if (aData & mask) { - DBG_PRINT("1"); - mark(aOneMarkMicros); - space(aOneSpaceMicros); - } else { - DBG_PRINT("0"); - mark(aZeroMarkMicros); - space(aZeroSpaceMicros); - } - } - DBG_PRINTLN(""); - } -#if defined(LSB_FIRST_REQUIRED) - else { // Send the Least Significant Bit (LSB) first / MSB last. - for (uint16_t bit = 0; bit < aNumberOfBits; bit++, aData >>= 1) - if (aData & 1) { // Send a 1 - DBG_PRINT("1"); - mark(aOneMarkMicros); - space(aOneSpaceMicros); - } else { // Send a 0 - DBG_PRINT("0"); - mark(aZeroMarkMicros); - space(aZeroSpaceMicros); - } - DBG_PRINTLN(""); - } -#endif -} - -//+============================================================================= -// Sends an IR mark for the specified number of microseconds. -// The mark output is modulated at the PWM frequency. -// - -void IRsend::mark(uint16_t timeMicros) { -#ifdef USE_SOFT_SEND_PWM - unsigned long start = micros(); - unsigned long stop = start + timeMicros; - if (stop + periodTimeMicros < start) { - // Counter wrap-around, happens very seldom, but CAN happen. - // Just give up instead of possibly damaging the hardware. - return; - } - unsigned long nextPeriodEnding = start; - unsigned long now = micros(); - while (now < stop) { - SENDPIN_ON(sendPin); - sleepMicros (periodOnTimeMicros); - SENDPIN_OFF(sendPin); - nextPeriodEnding += periodTimeMicros; - sleepUntilMicros(nextPeriodEnding); - now = micros(); - } -#elif defined(USE_NO_SEND_PWM) - digitalWrite(sendPin, LOW); // Set output to active low. -#else - TIMER_ENABLE_SEND_PWM; // Enable pin 3 PWM output -#endif - if (timeMicros > 0) { - delayMicroseconds(timeMicros); - } -} - -void IRsend::mark_long(uint32_t timeMicros) { -#if defined(USE_NO_SEND_PWM) - digitalWrite(sendPin, LOW); // Set output to active low. -#else - TIMER_ENABLE_SEND_PWM; // Enable pin 3 PWM output -#endif - if (timeMicros > 0) { - custom_delay_usec(timeMicros); - } -} - -//+============================================================================= -// Leave pin off for time (given in microseconds) -// Sends an IR space for the specified number of microseconds. -// A space is no output, so the PWM output is disabled. -// -void IRsend::space(uint16_t timeMicros) { -#if defined(USE_NO_SEND_PWM) - digitalWrite(sendPin, HIGH); // Set output to inactive high. -#else - TIMER_DISABLE_SEND_PWM; // Disable PWM output -#endif - if (timeMicros > 0) { - delayMicroseconds(timeMicros); - } -} - -/* - * used e.g. by LEGO - */ -void IRsend::space_long(uint32_t timeMicros) { -#if defined(USE_NO_SEND_PWM) - digitalWrite(sendPin, HIGH); // Set output to inactive high. -#else - TIMER_DISABLE_SEND_PWM; // Disable PWM output -#endif - if (timeMicros > 0) { - // custom delay does not work on an ATtiny85 with 1 MHz. It results in a delay of 760 us instead of the requested 560 us - custom_delay_usec(timeMicros); - } -} - -#ifdef USE_DEFAULT_ENABLE_IR_OUT -//+============================================================================= -// Enables IR output. The khz value controls the modulation frequency in kilohertz. -// The IR output will be on pin 3 (OC2B). -// This routine is designed for 36-40KHz; if you use it for other values, it's up to you -// to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.) -// TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B -// controlling the duty cycle. -// There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A) -// To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin. -// A few hours staring at the ATmega documentation and this will all make sense. -// See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details. -// -void IRsend::enableIROut(int khz) { -#ifdef USE_SOFT_SEND_PWM - periodTimeMicros = (1000U + khz / 2) / khz; // = 1000/khz + 1/2 = round(1000.0/khz) - periodOnTimeMicros = periodTimeMicros * IR_SEND_DUTY_CYCLE / 100U - PULSE_CORRECTION_MICROS; -#endif - -#if defined(USE_NO_SEND_PWM) - pinMode(sendPin, OUTPUT); - digitalWrite(sendPin, HIGH); // Set output to inactive high. -#else -// Disable the Timer2 Interrupt (which is used for receiving IR) - TIMER_DISABLE_RECEIVE_INTR; //Timer2 Overflow Interrupt - - pinMode(sendPin, OUTPUT); - - SENDPIN_OFF(sendPin); // When not sending, we want it low - - timerConfigForSend(khz); -#endif -} -#endif - -//+============================================================================= -// Custom delay function that circumvents Arduino's delayMicroseconds 16 bit limit -// It does not work on an ATtiny85 with 1 MHz. It results in a delay of 760 us instead of the requested 560 us - -void IRsend::custom_delay_usec(unsigned long uSecs) { - if (uSecs > 4) { - unsigned long start = micros(); - unsigned long endMicros = start + uSecs - 4; - if (endMicros < start) { // Check if overflow - while (micros() > start) { - } // wait until overflow - } - while (micros() < endMicros) { - } // normal wait - } -} - -#endif // SENDING_SUPPORTED diff --git a/src/ir_BangOlufsen.hpp b/src/ir_BangOlufsen.hpp new file mode 100644 index 000000000..628a5792e --- /dev/null +++ b/src/ir_BangOlufsen.hpp @@ -0,0 +1,496 @@ +/* + * ir_BangOlufsen.hpp + * + * Contains functions for receiving and sending Bang & Olufsen IR and Datalink '86 protocols + * To receive B&O and ENABLE_BEO_WITHOUT_FRAME_GAP is NOT defined, you must set RECORD_GAP_MICROS to + * at least 16000 to accommodate the unusually long 3. start space. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2022-2023 Daniel Wallner and Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_BANG_OLUFSEN_HPP +#define _IR_BANG_OLUFSEN_HPP + +//============================================================================== +// +// +// Bang & Olufsen +// +// +//============================================================================== +// https://www.mikrocontroller.net/attachment/33137/datalink.pdf +// https://www.mikrocontroller.net/articles/IRMP_-_english#B&O + +// This protocol is unusual in two ways: + +// 1. The carrier frequency is 455 kHz + +// You can build your own receiver as Bang & Olufsen did (check old schematics) or use a TSOP7000 +// Vishay stopped producing TSOP7000 since 2009 so you will probably only find counterfeits: +// https://www.vishay.com/files/whatsnew/doc/ff_FastFacts_CounterfeitTSOP7000_Dec72018.pdf +// It is also likely that you will need an oscilloscope to debug a counterfeit TSOP7000 +// The specimen used to test this code was very noisy and had a very low output current +// A somewhat working fix was to put a 4n7 capacitor across the output and ground followed by a pnp emitter follower +// Other examples may require a different treatment +// This particular receiver also did receive lower frequencies but rather poorly and with a lower delay than usual +// If you need to parallel a receiver with another one you may need to delay the signal to get in phase with the other receiver + +// 2. A stream of messages can be sent back to back with a new message immediately following the previous stop space + +// It might be that this only happens over IR and not on the datalink protocol +// You can choose to support this or not: + +// Mode 1: Mode with gaps between frames +// Set RECORD_GAP_MICROS to at least 16000 to accept the unusually long 3. start space +// Can only receive single messages. Back to back repeats will result in overflow + +// Mode 2: Break at start mode +// Define ENABLE_BEO_WITHOUT_FRAME_GAP and set RECORD_GAP_MICROS to less than 15000 +// to treat the 3. start space of 15.5 ms as a gap between messages, which makes decoding easier :-). +// The receiving of a transmission will then result in a dummy decode of the first 2 start bits with 0 bits data +// followed by a 15.5 ms gap and a data frame with one start bit (originally sent as 4. start bit). +// If the receiver is not resumed within a ms or so, partial messages will be decoded +// Debug printing in the wrong place is very likely to break reception +// Make sure to check the number of bits to filter dummy and incomplete messages + +// !!! We assume that the real implementations never set the official first header bit to anything other than 0 !!! +// !!! We therefore use 4 start bits instead of the specified 3 and in turn ignore the first header bit of the specification !!! + +// IR messages are 16 bits long. Datalink messages have different lengths. +// This implementation supports up to 40 bits total length split into 8 bit data/command and a header/address of variable length +// Header data with more than 16 bits is stored in decodedIRData.extra + +// B&O is a pulse distance protocol, but it has 3 bit values 0, 1 and (equal/repeat) as well as a special start and trailing bit. +// +// MSB first, 4 start bits + 8 to 16? bit address + 8 bit command + 1 special trailing bit + 1 stop bit. +// Address can be longer than 8 bit. + +/* + * Options for this decoder + * + */ +#define ENABLE_BEO_WITHOUT_FRAME_GAP // Requires additional 30 bytes program memory. Enabled by default, see https://github.com/Arduino-IRremote/Arduino-IRremote/discussions/1181 +//#define SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE // This also supports headers up to 32 bit. Requires additional 150 bytes program memory. +#if defined(DECODE_BEO) +# if defined(ENABLE_BEO_WITHOUT_FRAME_GAP) +# if RECORD_GAP_MICROS > 15000 +#warning If defined ENABLE_BEO_WITHOUT_FRAME_GAP, RECORD_GAP_MICROS must be set to <= 15000 by "#define RECORD_GAP_MICROS 12750" +# endif +# else +# if RECORD_GAP_MICROS < 16000 +#error If not defined ENABLE_BEO_WITHOUT_FRAME_GAP, RECORD_GAP_MICROS must be set to a value >= 16000 by "#define RECORD_GAP_MICROS 16000" +# endif +# endif +#endif + +#define BEO_DATA_BITS 8 // Command or character + +#define BEO_UNIT 3125 // All timings are in microseconds + +#define BEO_IR_MARK 200 // The length of a mark in the IR protocol +#define BEO_DATALINK_MARK (BEO_UNIT / 2) // The length of a mark in the Datalink protocol + +#define BEO_PULSE_LENGTH_ZERO BEO_UNIT // The length of a one to zero transition +#define BEO_PULSE_LENGTH_EQUAL (2 * BEO_UNIT) // 6250 The length of an equal bit +#define BEO_PULSE_LENGTH_ONE (3 * BEO_UNIT) // 9375 The length of a zero to one transition +#define BEO_PULSE_LENGTH_TRAILING_BIT (4 * BEO_UNIT) // 12500 The length of the stop bit +#define BEO_PULSE_LENGTH_START_BIT (5 * BEO_UNIT) // 15625 The length of the start bit +// It is not allowed to send two ones or zeros, you must send a one or zero and a equal instead. + +//#define BEO_LOCAL_DEBUG +//#define BEO_LOCAL_TRACE + +#ifdef BEO_LOCAL_DEBUG +# define BEO_DEBUG_PRINT(...) Serial.print(__VA_ARGS__) +# define BEO_DEBUG_PRINTLN(...) Serial.println(__VA_ARGS__) +#else +# define BEO_DEBUG_PRINT(...) void() +# define BEO_DEBUG_PRINTLN(...) void() +#endif + +#ifdef BEO_LOCAL_TRACE +# undef BEO_TRACE_PRINT +# undef BEO_TRACE_PRINTLN +# define BEO_TRACE_PRINT(...) Serial.print(__VA_ARGS__) +# define BEO_TRACE_PRINTLN(...) Serial.println(__VA_ARGS__) +#else +# define BEO_TRACE_PRINT(...) void() +# define BEO_TRACE_PRINTLN(...) void() +#endif + +/************************************ + * Start of send and decode functions + ************************************/ + +/* + * TODO aNumberOfRepeats are handled not correctly if ENABLE_BEO_WITHOUT_FRAME_GAP is defined + */ +void IRsend::sendBangOlufsen(uint16_t aHeader, uint8_t aData, int_fast8_t aNumberOfRepeats, int8_t aNumberOfHeaderBits) { + for (int_fast8_t i = 0; i < aNumberOfRepeats + 1; ++i) { + sendBangOlufsenRaw((uint32_t(aHeader) << 8) | aData, aNumberOfHeaderBits + 8, i != 0); + } +} + +void IRsend::sendBangOlufsenDataLink(uint32_t aHeader, uint8_t aData, int_fast8_t aNumberOfRepeats, int8_t aNumberOfHeaderBits) { + for (int_fast8_t i = 0; i < aNumberOfRepeats + 1; ++i) { + sendBangOlufsenRawDataLink((uint64_t(aHeader) << 8) | aData, aNumberOfHeaderBits + 8, i != 0, true); + } +} + +/* + * @param aBackToBack If true send data back to back, which cannot be decoded if ENABLE_BEO_WITHOUT_FRAME_GAP is NOT defined + */ +void IRsend::sendBangOlufsenRaw(uint32_t aRawData, int_fast8_t aBits, bool aBackToBack) { +#if defined(USE_NO_SEND_PWM) || defined(SEND_PWM_BY_TIMER) || BEO_KHZ == 38 // BEO_KHZ == 38 is for unit test which runs the B&O protocol with 38 kHz + + /* + * 455 kHz PWM is currently only supported with SEND_PWM_BY_TIMER defined, otherwise maximum is 180 kHz + */ +# if !defined(USE_NO_SEND_PWM) +# if defined(SEND_PWM_BY_TIMER) + enableHighFrequencyIROut (BEO_KHZ); +# elif (BEO_KHZ == 38) + enableIROut (BEO_KHZ); // currently only for unit test +# endif +# endif + +// AGC / Start - 3 bits + first constant 0 header bit described in the official documentation + if (!aBackToBack) { + mark(BEO_IR_MARK); + } + space(BEO_PULSE_LENGTH_ZERO - BEO_IR_MARK); + mark(BEO_IR_MARK); + space(BEO_PULSE_LENGTH_ZERO - BEO_IR_MARK); + mark(BEO_IR_MARK); + space(BEO_PULSE_LENGTH_START_BIT - BEO_IR_MARK); + +// First bit of header is assumed to be a constant 0 to have a fixed state to begin with the equal decisions. +// So this first 0 is treated as the last bit of AGC + mark(BEO_IR_MARK); + space(BEO_PULSE_LENGTH_ZERO - BEO_IR_MARK); + bool tLastBitValueWasOne = false; + +// Header / Data + uint32_t mask = 1UL << (aBits - 1); + for (; mask; mask >>= 1) { + if (tLastBitValueWasOne && !(aRawData & mask)) { + mark(BEO_IR_MARK); + space(BEO_PULSE_LENGTH_ZERO - BEO_IR_MARK); + tLastBitValueWasOne = false; + } else if (!tLastBitValueWasOne && (aRawData & mask)) { + mark(BEO_IR_MARK); + space(BEO_PULSE_LENGTH_ONE - BEO_IR_MARK); + tLastBitValueWasOne = true; + } else { + mark(BEO_IR_MARK); + space(BEO_PULSE_LENGTH_EQUAL - BEO_IR_MARK); + } + } + +// Stop + mark(BEO_IR_MARK); + space(BEO_PULSE_LENGTH_TRAILING_BIT - BEO_IR_MARK); + mark(BEO_IR_MARK); + +#else + (void) aRawData; + (void) aBits; + (void) aBackToBack; +#endif +} + +/* + * Version with 64 bit aRawData, which can send both timings, but costs more program memory + * @param aBackToBack If true send data back to back, which cannot be decoded if ENABLE_BEO_WITHOUT_FRAME_GAP is NOT defined + * @param aUseDatalinkTiming if false it does the same as sendBangOlufsenRaw() + */ +void IRsend::sendBangOlufsenRawDataLink(uint64_t aRawData, int_fast8_t aBits, bool aBackToBack, bool aUseDatalinkTiming) { +#if defined(USE_NO_SEND_PWM) || BEO_KHZ == 38 // BEO_KHZ == 38 is for unit test which runs the B&O protocol with 38 kHz instead 0f 455 kHz + uint16_t tSendBEOMarkLength = aUseDatalinkTiming ? BEO_DATALINK_MARK : BEO_IR_MARK; + + /* + * 455 kHz PWM is currently not supported, maximum is 180 kHz + */ +#if !defined(USE_NO_SEND_PWM) + enableIROut (BEO_KHZ); +#endif + +// AGC / Start - 3 bits + first constant 0 header bit described in the official documentation + if (!aBackToBack) { + mark(tSendBEOMarkLength); + } + space(BEO_PULSE_LENGTH_ZERO - tSendBEOMarkLength); + mark(tSendBEOMarkLength); + space(BEO_PULSE_LENGTH_ZERO - tSendBEOMarkLength); + mark(tSendBEOMarkLength); + space(BEO_PULSE_LENGTH_START_BIT - tSendBEOMarkLength); + +// First bit of header is assumed to be a constant 0 to have a fixed state to begin with the equal decisions. +// So this first 0 is treated as the last bit of AGC + mark(tSendBEOMarkLength); + space(BEO_PULSE_LENGTH_ZERO - tSendBEOMarkLength); + bool tLastBitValueWasOne = false; + +// Header / Data + uint32_t mask = 1UL << (aBits - 1); + for (; mask; mask >>= 1) { + if (tLastBitValueWasOne && !(aRawData & mask)) { + mark(tSendBEOMarkLength); + space(BEO_PULSE_LENGTH_ZERO - tSendBEOMarkLength); + tLastBitValueWasOne = false; + } else if (!tLastBitValueWasOne && (aRawData & mask)) { + mark(tSendBEOMarkLength); + space(BEO_PULSE_LENGTH_ONE - tSendBEOMarkLength); + tLastBitValueWasOne = true; + } else { + mark(tSendBEOMarkLength); + space(BEO_PULSE_LENGTH_EQUAL - tSendBEOMarkLength); + } + } + +// Stop + mark(tSendBEOMarkLength); + space(BEO_PULSE_LENGTH_TRAILING_BIT - tSendBEOMarkLength); + mark(tSendBEOMarkLength); + +#else + (void) aRawData; + (void) aBits; + (void) aUseDatalinkTiming; + (void) aBackToBack; +#endif +} + +#define BEO_MATCH_DELTA (BEO_UNIT / 2 - MICROS_PER_TICK) +static bool matchBeoLength(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) { + const uint16_t tMeasuredMicros = aMeasuredTicks * MICROS_PER_TICK; + return aMatchValueMicros - BEO_MATCH_DELTA < tMeasuredMicros && tMeasuredMicros < aMatchValueMicros + BEO_MATCH_DELTA; +} + +bool IRrecv::decodeBangOlufsen() { +#if defined(ENABLE_BEO_WITHOUT_FRAME_GAP) + if (decodedIRData.rawlen != 6 && decodedIRData.rawlen < 36) { // 16 bits minimum +#else + if (decodedIRData.rawlen < 44) { // 16 bits minimum +#endif + return false; + } + +#if defined(SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE) + uint16_t protocolMarkLength = 0; // contains BEO_IR_MARK or BEO_DATALINK_MARK depending of 4. mark received + uint64_t tDecodedRawData = 0; +#else + uint32_t tDecodedRawData = 0; +#endif + uint8_t tLastDecodedBitValue = 0; // the last start bit is assumed to be zero + uint8_t tPulseNumber = 0; + uint8_t tBitNumber = 0; + + BEO_TRACE_PRINT(F("Pre gap: ")); + BEO_TRACE_PRINT(decodedIRData.initialGapTicks * 50); + BEO_TRACE_PRINT(F(" raw len: ")); + BEO_TRACE_PRINTLN(decodedIRData.rawlen); + +#if defined(ENABLE_BEO_WITHOUT_FRAME_GAP) + /* + * Check if we have the AGC part of the first frame, i.e. start bit 1 and 2. + */ + if (decodedIRData.rawlen == 6) { + if ((matchMark(decodedIRData.rawDataPtr->rawbuf[3], BEO_IR_MARK) + || matchMark(decodedIRData.rawDataPtr->rawbuf[3], BEO_DATALINK_MARK)) + && (matchSpace(decodedIRData.rawDataPtr->rawbuf[4], BEO_PULSE_LENGTH_ZERO - BEO_IR_MARK) + || matchSpace(decodedIRData.rawDataPtr->rawbuf[4], BEO_PULSE_LENGTH_ZERO - BEO_DATALINK_MARK))) { + BEO_TRACE_PRINT(::getProtocolString(BANG_OLUFSEN)); + BEO_TRACE_PRINTLN(F("B&O: AGC only part (start bits 1 + 2 of 4) detected")); + } else { + return false; // no B&O protocol + } + } else { + /* + * Check if leading gap is trailing bit of first frame + */ + if (!matchSpace(decodedIRData.initialGapTicks, BEO_PULSE_LENGTH_START_BIT)) { + BEO_TRACE_PRINT(::getProtocolString(BANG_OLUFSEN)); + BEO_TRACE_PRINTLN(F(": Leading gap is wrong")); // Leading gap is trailing bit of first frame + return false; // no B&O protocol + } + + if (matchMark(decodedIRData.rawDataPtr->rawbuf[1], BEO_IR_MARK)) { +# if defined(SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE) + protocolMarkLength = BEO_IR_MARK; + } else if (matchMark(decodedIRData.rawDataPtr->rawbuf[1], BEO_DATALINK_MARK)) { + protocolMarkLength = BEO_DATALINK_MARK; +# endif + } else { + BEO_TRACE_PRINT(::getProtocolString(BANG_OLUFSEN)); + BEO_TRACE_PRINTLN(F(": mark length is wrong")); + return false; + } + + // skip first zero header bit + for (uint8_t tRawBufferMarkIndex = 3; tRawBufferMarkIndex < decodedIRData.rawlen; tRawBufferMarkIndex += 2) { +#else + for (uint8_t tRawBufferMarkIndex = 1; tRawBufferMarkIndex < decodedIRData.rawlen; tRawBufferMarkIndex += 2) { +#endif // defined(ENABLE_BEO_WITHOUT_FRAME_GAP) + + uint16_t markLength = decodedIRData.rawDataPtr->rawbuf[tRawBufferMarkIndex]; + uint16_t spaceLength = decodedIRData.rawDataPtr->rawbuf[tRawBufferMarkIndex + 1]; + + BEO_TRACE_PRINT(tPulseNumber); + BEO_TRACE_PRINT(' '); + BEO_TRACE_PRINT(markLength * MICROS_PER_TICK); + BEO_TRACE_PRINT(' '); + BEO_TRACE_PRINT(spaceLength * MICROS_PER_TICK); + BEO_TRACE_PRINT(F(" (")); + BEO_TRACE_PRINT((markLength + spaceLength) * MICROS_PER_TICK); + BEO_TRACE_PRINTLN(F(") ")); + +#if !defined(ENABLE_BEO_WITHOUT_FRAME_GAP) + /* + * Handle the first 4 start bits + * Check if the 3. bit is the long start bit. If we see the long start bit earlier, synchronize bit counter. + */ + if (tPulseNumber < 4) { + if (tPulseNumber < 2) { + // bit 0 and 1 + if (matchSpace(spaceLength, BEO_PULSE_LENGTH_START_BIT - BEO_IR_MARK)) { + BEO_TRACE_PRINTLN(F(": detected long start bit -> synchronize state now")); + tPulseNumber = 2; + } + } else { + if (tPulseNumber == 3) { + if (matchMark(markLength, BEO_IR_MARK)) { +# if defined(SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE) + protocolMarkLength = BEO_IR_MARK; + } else if (matchMark(markLength, BEO_DATALINK_MARK)) { + protocolMarkLength = BEO_DATALINK_MARK; +# endif + } else { + BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN)); + BEO_DEBUG_PRINTLN(F(": 4. (start) mark length is wrong")); + return false; + } + } + // bit 2 and 3 + if (!matchBeoLength(markLength + spaceLength, + (tPulseNumber == 2) ? BEO_PULSE_LENGTH_START_BIT : BEO_PULSE_LENGTH_ZERO)) { + BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN)); + BEO_DEBUG_PRINTLN(F(": Start length is wrong")); + return false; + } + } + } else { +#endif // !defined(ENABLE_BEO_WITHOUT_FRAME_GAP) + + /* + * Decode header / data + * First check for length of mark + */ +#if defined(SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE) + if (!matchMark(markLength, protocolMarkLength)) { +#else + if (!matchMark(markLength, BEO_IR_MARK)) { +#endif + BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN)); + BEO_DEBUG_PRINTLN(F(": Mark length is wrong")); + return false; + } + + /* + * Check for stop after receiving at least 8 bits for data and 4 bits for header + */ + if (tBitNumber > BEO_DATA_BITS + 4) { + if (matchBeoLength(markLength + spaceLength, BEO_PULSE_LENGTH_TRAILING_BIT)) { + BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN)); + BEO_DEBUG_PRINTLN(F(": Trailing bit detected")); + break; + } +#if !defined(ENABLE_BEO_WITHOUT_FRAME_GAP) + if (tRawBufferMarkIndex >= decodedIRData.rawlen - 3) { // (rawlen - 3) is index of trailing bit mark + BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN)); + BEO_DEBUG_PRINTLN(F(": End of buffer, but no trailing bit detected")); + return false; + } +#endif + } + + /* + * Decode bit + */ + if (tLastDecodedBitValue == 0 && matchBeoLength(markLength + spaceLength, BEO_PULSE_LENGTH_ONE)) { + tLastDecodedBitValue = 1; + } else if (tLastDecodedBitValue == 1 && matchBeoLength(markLength + spaceLength, BEO_PULSE_LENGTH_ZERO)) { + tLastDecodedBitValue = 0; + } else if (!matchBeoLength(markLength + spaceLength, BEO_PULSE_LENGTH_EQUAL)) { + BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN)); + BEO_DEBUG_PRINT(F(": Index=")); + BEO_DEBUG_PRINT(tRawBufferMarkIndex); + BEO_DEBUG_PRINT(F(" Length ")); + BEO_DEBUG_PRINT((markLength + spaceLength) * MICROS_PER_TICK); + BEO_DEBUG_PRINTLN(F(" is wrong")); + return false; + } + tDecodedRawData <<= 1; + tDecodedRawData |= tLastDecodedBitValue; + ++tBitNumber; + BEO_TRACE_PRINT(F("Bits ")); + BEO_TRACE_PRINT(tBitNumber); + BEO_TRACE_PRINT(F(" ")); + BEO_TRACE_PRINT(uint32_t(tDecodedRawData >> BEO_DATA_BITS), HEX); + BEO_TRACE_PRINT(F(" ")); + BEO_TRACE_PRINTLN(uint8_t(tDecodedRawData & ((1 << BEO_DATA_BITS) - 1)), HEX); + // End of bit decode +#if !defined(ENABLE_BEO_WITHOUT_FRAME_GAP) + } + +#else + /* + * Check for last bit after decoding it + */ + if (tRawBufferMarkIndex >= decodedIRData.rawlen - 3) { // (rawlen - 3) is index of last bit mark + BEO_TRACE_PRINT(::getProtocolString(BANG_OLUFSEN)); + BEO_TRACE_PRINTLN(F(": Last bit reached")); + break; + } +#endif + + ++tPulseNumber; + } +#if defined(ENABLE_BEO_WITHOUT_FRAME_GAP) + } +#endif + + decodedIRData.protocol = BANG_OLUFSEN; + decodedIRData.address = tDecodedRawData >> BEO_DATA_BITS; // lower header tBitNumber + decodedIRData.command = tDecodedRawData & ((1 << BEO_DATA_BITS) - 1); // lower 8 tBitNumber + decodedIRData.extra = tDecodedRawData >> (BEO_DATA_BITS + 16); // upper header tBitNumber + decodedIRData.numberOfBits = tBitNumber; + decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; + decodedIRData.decodedRawData = tDecodedRawData; + + return true; +} +#endif // _IR_BANG_OLUFSEN_HPP diff --git a/src/ir_BoseWave.cpp b/src/ir_BoseWave.cpp deleted file mode 100644 index d0f3cee7d..000000000 --- a/src/ir_BoseWave.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "IRremote.h" - -//============================================================================== -// BBBB OOO SSSS EEEEE -// B B O O S E -// BB B O O SSS EEEE -// B B O O S E -// BBBB OOO SSSS EEEEE -//============================================================================== -// -// Bose Wave Radio CD Remote Control -// |-------------------------------------| -// | On/Off Sleep VolUp | -// | Play/Pause Stop VolDown | -// | FM AM Aux | -// | Tune Down Tune Up Mute | -// | 1 2 3 | -// | 4 5 6 | -// |-------------------------------------| -// -// Support for Bose Wave Radio CD provided by https://github.com/uvotguy. -// -// This protocol was reverse engineered by capturing IR signals from a working -// remote. Multiple signals were captured on my oscilloscope, and the timing -// values were averaged. -// -// IR codes are 8 bits. Transmission starts with a header: a mark and a space. -// The header is followed by an 8-bit command, where a bit is a mark and a short -// space (1) or a long space (0). The command is followed by the complement of -// the command (8 bits). A transmission ends with a short mark. -// -// As seen on my trusty oscilloscope, there is no repeat code. Instead, when I -// press and hold a button on my remote, it sends a command, makes a 51.2ms space, -// and resends the command, etc, etc. -// -// It may be worth noting that these values do NOT match those in the LIRC -// remote database (http://lirc.sourceforge.net/remotes/bose/). - -#define CMD_ON_OFF 0xff -#define CMD_MUTE 0xfe -#define CMD_VOL_UP 0xfd -#define CMD_VOL_DOWN 0xfc -#define CMD_PRESET_6 0xfb -#define CMD_SLEEP 0xfa -#define CMD_FM 0xf9 -#define CMD_AUX 0xf8 -#define CMD_AM 0xf7 -#define CMD_PLAY_PAUSE 0xf6 -#define CMD_STOP 0xf5 -#define CMD_TUNE_UP 0xf4 -#define CMD_TUNE_DOWN 0xf3 -#define CMD_PRESET_1 0xf2 -#define CMD_PRESET_2 0xf1 -#define CMD_PRESET_3 0xf0 -#define CMD_PRESET_4 0xef -#define CMD_PRESET_5 0xee - -#define BOSEWAVE_BITS 8 -#define BOSEWAVE_HEADER_MARK 1061 -#define BOSEWAVE_HEADER_SPACE 1456 -#define BOSEWAVE_BIT_MARK 534 -#define BOSEWAVE_ONE_SPACE 468 -#define BOSEWAVE_ZERO_SPACE 1447 -#define BOSEWAVE_END_MARK 614 -#define BOSEWAVE_REPEAT_SPACE 51200 // Not used. - -//+============================================================================= -#if SEND_BOSEWAVE -unsigned int rawSignal[35]; -void IRsend::sendBoseWave(unsigned char code) { - - int index = 0; - // Header - rawSignal[index++] = BOSEWAVE_HEADER_MARK; - rawSignal[index++] = BOSEWAVE_HEADER_SPACE; - - // 8 bit command - for (unsigned char mask = 0x80; mask; mask >>= 1) { - rawSignal[index++] = BOSEWAVE_BIT_MARK; - if (code & mask) { - rawSignal[index++] = BOSEWAVE_ONE_SPACE; - } else { - rawSignal[index++] = BOSEWAVE_ZERO_SPACE; - } - } - - // 8 bit command complement - for (unsigned char mask = 0x80; mask; mask >>= 1) { - rawSignal[index++] = BOSEWAVE_BIT_MARK; - if (code & mask) { - rawSignal[index++] = BOSEWAVE_ZERO_SPACE; - } else { - rawSignal[index++] = BOSEWAVE_ONE_SPACE; - } - } - // End transmission - rawSignal[index++] = BOSEWAVE_END_MARK; - - // Transmit - this->sendRaw(rawSignal, 35, 38); -} -#endif - -//+============================================================================= -#if DECODE_BOSEWAVE -bool IRrecv::decodeBoseWave() { - unsigned char command = 0; // Decoded command - unsigned char complement = 0; // Decoded command complement - - int index = 0; // Index in to results array - - DBG_PRINTLN("Decoding Bose Wave ..."); - - // Check we have enough data - if (irparams.rawlen < (2 * BOSEWAVE_BITS * 2) + 3) { - DBG_PRINT("\tInvalid data length found: "); - DBG_PRINTLN(results.rawlen); - return false; - } - - // Check header "mark" - index = 1; - if (!MATCH_MARK(results.rawbuf[index], BOSEWAVE_HEADER_MARK)) { - DBG_PRINT("\tInvalid Header Mark. Expecting "); - DBG_PRINT(BOSEWAVE_HEADER_MARK); - DBG_PRINT(". Got "); - DBG_PRINTLN(results.rawbuf[index] * MICROS_PER_TICK); - return false; - } - index++; - - // Check header "space" - if (!MATCH_SPACE(results.rawbuf[index], BOSEWAVE_HEADER_SPACE)) { - DBG_PRINT("\tInvalid Header Space. Expecting "); - DBG_PRINT(BOSEWAVE_HEADER_SPACE); - DBG_PRINT(". Got "); - DBG_PRINTLN(results.rawbuf[index] * MICROS_PER_TICK); - return false; - } - index++; - - // Decode the data bits - for (int ii = 7; ii >= 0; ii--) { - // Check bit "mark". Mark is always the same length. - if (!MATCH_MARK(results.rawbuf[index], BOSEWAVE_BIT_MARK)) { - DBG_PRINT("\tInvalid command Mark. Expecting "); - DBG_PRINT(BOSEWAVE_BIT_MARK); - DBG_PRINT(". Got "); - DBG_PRINTLN(results.rawbuf[index] * MICROS_PER_TICK); - return false; - } - index++; - - // Check bit "space" - if (MATCH_SPACE(results.rawbuf[index], BOSEWAVE_ONE_SPACE)) { - command |= (0x01 << ii); - } else if (MATCH_SPACE(results.rawbuf[index], BOSEWAVE_ZERO_SPACE)) { - // Nothing to do for zeroes. - } else { - DBG_PRINT("\tInvalid command Space. Got "); - DBG_PRINTLN(results.rawbuf[index] * MICROS_PER_TICK); - return false; - } - index++; - } - - // Decode the command complement bits. We decode it here as the complement - // of the complement (0=1 and 1=0) so we can easily compare it to the command. - for (int ii = 7; ii >= 0; ii--) { - // Check bit "mark". Mark is always the same length. - if (!MATCH_MARK(results.rawbuf[index], BOSEWAVE_BIT_MARK)) { - DBG_PRINT("\tInvalid complement Mark. Expecting "); - DBG_PRINT(BOSEWAVE_BIT_MARK); - DBG_PRINT(". Got "); - DBG_PRINTLN(results.rawbuf[index] * MICROS_PER_TICK); - return false; - } - index++; - - // Check bit "space" - if (MATCH_SPACE(results.rawbuf[index], BOSEWAVE_ONE_SPACE)) { - // Nothing to do. - } else if (MATCH_SPACE(results.rawbuf[index], BOSEWAVE_ZERO_SPACE)) { - complement |= (0x01 << ii); - } else { - DBG_PRINT("\tInvalid complement Space. Got "); - DBG_PRINTLN(results.rawbuf[index] * MICROS_PER_TICK); - return false; - } - index++; - } - - if (command != complement) { - DBG_PRINT("\tComplement is not correct. Command=0x"); - DBG_PRINT(command, HEX); - DBG_PRINT(" Complement=0x"); - DBG_PRINTLN(complement, HEX); - return false; - } else { - DBG_PRINTLN("\tValid command"); - } - - // Check end "mark" - if (MATCH_MARK(results.rawbuf[index], BOSEWAVE_END_MARK) == 0) { - DBG_PRINT("\tInvalid end Mark. Got "); - DBG_PRINTLN(results.rawbuf[index] * MICROS_PER_TICK); - return false; - } - - // Success - results.bits = BOSEWAVE_BITS; - results.value = command; - results.decode_type = BOSEWAVE; - - return true; -} -bool IRrecv::decodeBoseWave(decode_results *aResults) { - bool aReturnValue = decodeBoseWave(); - *aResults = results; - return aReturnValue; -} -#endif diff --git a/src/ir_BoseWave.hpp b/src/ir_BoseWave.hpp new file mode 100644 index 000000000..336d886f0 --- /dev/null +++ b/src/ir_BoseWave.hpp @@ -0,0 +1,123 @@ +/* + * ir_BoseWave.cpp + * + * Contains functions for receiving and sending Bose IR Protocol + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + */ +#ifndef _IR_BOSEWAVE_HPP +#define _IR_BOSEWAVE_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +//============================================================================== +// BBBB OOO SSSS EEEEE +// B B O O S E +// BB B O O SSS EEEE +// B B O O S E +// BBBB OOO SSSS EEEEE +//============================================================================== +// see http://lirc.sourceforge.net/remotes/bose/WAVERADIO +// see: https://www.mikrocontroller.net/articles/IRMP_-_english#BOSE +// +// Support for Bose Wave Radio CD initially provided by https://github.com/uvotguy. +// +// As seen on my oscilloscope, there is no repeat code. Instead, when I +// press and hold a button on my remote, it sends a command, makes a 51.2ms space, +// and resends the command again, and so on. +// 38 kHz, LSB first, 1 start bit + 8 bit data + 8 bit inverted data + 1 stop bit. +#define BOSEWAVE_BITS 16 // Command and inverted command + +#define BOSEWAVE_HEADER_MARK 1014 // 1014 are 39 clock periods (I counted 3 times!) +#define BOSEWAVE_HEADER_SPACE 1468 // 1468(measured), 1456 are 56 clock periods +#define BOSEWAVE_BIT_MARK 520 // 520 are 20 clock periods +#define BOSEWAVE_ZERO_SPACE 468 // 468 are 18 clock periods +#define BOSEWAVE_ONE_SPACE 1468 // 1468(measured), 1456 are 56 clock periods + +#define BOSEWAVE_REPEAT_PERIOD 75000 +#define BOSEWAVE_REPEAT_DISTANCE 50000 +#define BOSEWAVE_MAXIMUM_REPEAT_DISTANCE 62000 + +struct PulseDistanceWidthProtocolConstants const BoseWaveProtocolConstants PROGMEM = {BOSEWAVE, BOSEWAVE_KHZ, BOSEWAVE_HEADER_MARK, + BOSEWAVE_HEADER_SPACE, BOSEWAVE_BIT_MARK, BOSEWAVE_ONE_SPACE, BOSEWAVE_BIT_MARK, BOSEWAVE_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST + , (BOSEWAVE_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr}; + +/************************************ + * Start of send and decode functions + ************************************/ + +void IRsend::sendBoseWave(uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + + // send 8 command bits and then 8 inverted command bits LSB first + uint16_t tData = ((~aCommand) << 8) | aCommand; + sendPulseDistanceWidth_P(&BoseWaveProtocolConstants, tData, BOSEWAVE_BITS, aNumberOfRepeats); +} + +bool IRrecv::decodeBoseWave() { + + if (!checkHeader_P(&BoseWaveProtocolConstants)) { + return false; + } + + // Check we have enough data +4 for initial gap, start bit mark and space + stop bit mark + if (decodedIRData.rawlen != (2 * BOSEWAVE_BITS) + 4) { + IR_DEBUG_PRINT(F("Bose: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not 36")); + return false; + } + + if (!decodePulseDistanceWidthData_P(&BoseWaveProtocolConstants, BOSEWAVE_BITS)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Bose: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } + + // Stop bit + if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * BOSEWAVE_BITS)], BOSEWAVE_BIT_MARK)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Bose: ")); + Serial.println(F("Stop bit mark length is wrong")); +#endif + return false; + } + + // Success +// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value + uint16_t tDecodedValue = decodedIRData.decodedRawData; + uint8_t tCommandNotInverted = tDecodedValue & 0xFF; // comes first and is in the lower bits (LSB first :-)) + uint8_t tCommandInverted = tDecodedValue >> 8; + // parity check for command. Use this variant to avoid compiler warning "comparison of promoted ~unsigned with unsigned [-Wsign-compare]" + if ((tCommandNotInverted ^ tCommandInverted) != 0xFF) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Bose: ")); + Serial.println(F("Command and inverted command check failed")); +#endif + return false; + } + decodedIRData.command = tCommandNotInverted; + decodedIRData.numberOfBits = BOSEWAVE_BITS; + decodedIRData.protocol = BOSEWAVE; + + // check for repeat + checkForRepeatSpaceTicksAndSetFlag(BOSEWAVE_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK); + + return true; +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_BOSEWAVE_HPP diff --git a/src/ir_Denon.cpp b/src/ir_Denon.cpp deleted file mode 100644 index 2019ee1a0..000000000 --- a/src/ir_Denon.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "IRremote.h" - -// Reverse Engineered by looking at RAW dumps generated by IRremote - -// I have since discovered that Denon publish all their IR codes: -// https://www.google.co.uk/search?q=DENON+MASTER+IR+Hex+Command+Sheet -// -> http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls - -// Having looked at the official Denon Pronto sheet and reverse engineered -// the timing values from it, it is obvious that Denon have a range of -// different timings and protocols ...the values here work for my AVR-3801 Amp! - -//============================================================================== -// DDDD EEEEE N N OOO N N -// D D E NN N O O NN N -// D D EEE N N N O O N N N -// D D E N NN O O N NN -// DDDD EEEEE N N OOO N N -//============================================================================== - -#define DENON_BITS 14 // The number of bits in the command - -#define DENON_HEADER_MARK 300 // The length of the Header:Mark -#define DENON_HEADER_SPACE 750 // The lenght of the Header:Space - -#define DENON_BIT_MARK 300 // The length of a Bit:Mark -#define DENON_ONE_SPACE 1800 // The length of a Bit:Space for 1's -#define DENON_ZERO_SPACE 750 // The length of a Bit:Space for 0's - -//+============================================================================= -// -#if SEND_DENON -void IRsend::sendDenon(unsigned long data, int nbits) { - // Set IR carrier frequency - enableIROut(38); - - // Header - mark(DENON_HEADER_MARK); - space(DENON_HEADER_SPACE); - - // Data - sendPulseDistanceWidthData(DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, data, nbits); - -// Footer - mark(DENON_BIT_MARK); - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -// -#if DECODE_DENON -bool IRrecv::decodeDenon() { - int offset = 1; // Skip the gap reading - - // Check we have the right amount of data - if (irparams.rawlen != 1 + 2 + (2 * DENON_BITS) + 1) { - return false; - } - - // Check initial Mark+Space match - if (!MATCH_MARK(results.rawbuf[offset], DENON_HEADER_MARK)) { - return false; - } - offset++; - - if (!MATCH_SPACE(results.rawbuf[offset], DENON_HEADER_SPACE)) { - return false; - } - offset++; - - // Read the bits in - if (!decodePulseDistanceData(DENON_BITS, offset, DENON_BIT_MARK, DENON_ONE_SPACE, DENON_ZERO_SPACE)) { - return false; - } - - // Success - results.bits = DENON_BITS; - results.decode_type = DENON; - return true; -} - -bool IRrecv::decodeDenon(decode_results *aResults) { - bool aReturnValue = decodeDenon(); - *aResults = results; - return aReturnValue; -} -#endif diff --git a/src/ir_Denon.hpp b/src/ir_Denon.hpp new file mode 100644 index 000000000..bfbe45caa --- /dev/null +++ b/src/ir_Denon.hpp @@ -0,0 +1,327 @@ +/* + * ir_Denon.cpp + * + * Contains functions for receiving and sending Denon/Sharp IR Protocol + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2023 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_DENON_HPP +#define _IR_DENON_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +//============================================================================== +// DDDD EEEEE N N OOO N N +// D D E NN N O O NN N +// D D EEE N N N O O N N N +// D D E N NN O O N NN +// DDDD EEEEE N N OOO N N +//============================================================================== +// SSSS H H AAA RRRR PPPP +// S H H A A R R P P +// SSS HHHHH AAAAA RRRR PPPP +// S H H A A R R P +// SSSS H H A A R R P +//============================================================================== +/* + Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0xED1 15 bits LSB first + + 200,-1800 + 300,- 750 + 300,- 800 + 200,- 800 + + 250,-1800 + 250,- 800 + 250,-1800 + 300,-1750 + + 300,- 750 + 300,-1800 + 250,-1800 + 250,-1850 + + 250,- 750 + 300,- 800 + 250,- 800 + 250 + Sum: 23050 + + Denon/Sharp variant + Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0x4ED1 15 bits LSB first + + 200,-1800 + 300,- 750 + 250,- 800 + 250,- 750 + + 300,-1800 + 250,- 800 + 250,-1800 + 300,-1750 + + 300,- 750 + 300,-1800 + 250,-1800 + 250,-1800 + + 300,- 750 + 300,- 750 + 300,-1800 + 250 + Sum: 23050 + */ +/* + * https://www.mikrocontroller.net/articles/IRMP_-_english#DENON + * Denon published all their IR codes: + * http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls + * Example: + * 0000 006D 0000 0020 000A 001E 000A 0046 000A 001E 000A 001E 000A 001E // 5 address bits + * 000A 001E 000A 001E 000A 0046 000A 0046 000A 0046 000A 001E 000A 0046 000A 0046 // 8 command bits + * 000A 001E 000A 001E 000A 0679 // 2 frame bits 0,0 + stop bit + space for AutoRepeat + * 000A 001E 000A 0046 000A 001E 000A 001E 000A 001E // 5 address bits + * 000A 0046 000A 0046 000A 001E 000A 001E 000A 001E 000A 0046 000A 001E 000A 001E // 8 inverted command bits + * 000A 0046 000A 0046 000A 0679 // 2 frame bits 1,1 + stop bit + space for Repeat + * From analyzing the codes for Tuner preset 1 to 8 in tab Main Zone ID#1 it is obvious, that the protocol is LSB first at least for command. + * All Denon codes with 32 as 3. value use the Kaseikyo Denon variant. + */ +// LSB first, no start bit, 5 address + 8 command + 2 frame (0,0) + 1 stop bit - each frame 2 times +// Every frame is auto repeated with a space period of 45 ms and the command and frame inverted to (1,1) or (0,1) for SHARP. +// +#define DENON_ADDRESS_BITS 5 +#define DENON_COMMAND_BITS 8 +#define DENON_FRAME_BITS 2 // 00/10 for 1. frame Denon/Sharp, inverted for autorepeat frame + +#define DENON_BITS (DENON_ADDRESS_BITS + DENON_COMMAND_BITS + DENON_FRAME_BITS) // 15 - The number of bits in the command +#define DENON_UNIT 260 + +#define DENON_BIT_MARK DENON_UNIT // The length of a Bit:Mark +#define DENON_ONE_SPACE (7 * DENON_UNIT) // 1820 // The length of a Bit:Space for 1's +#define DENON_ZERO_SPACE (3 * DENON_UNIT) // 780 // The length of a Bit:Space for 0's + +#define DENON_AUTO_REPEAT_DISTANCE 45000 // Every frame is auto repeated with a space period of 45 ms and the command and frame inverted. +#define DENON_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down. + +// for old decoder +#define DENON_HEADER_MARK DENON_UNIT // The length of the Header:Mark +#define DENON_HEADER_SPACE (3 * DENON_UNIT) // 780 // The length of the Header:Space + +struct PulseDistanceWidthProtocolConstants const DenonProtocolConstants PROGMEM = {DENON, DENON_KHZ, DENON_HEADER_MARK, DENON_HEADER_SPACE, + DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, + (DENON_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr}; + +/************************************ + * Start of send and decode functions + ************************************/ + +void IRsend::sendSharp(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + sendDenon(aAddress, aCommand, aNumberOfRepeats, 1); + // see https://github.com/Arduino-IRremote/Arduino-IRremote/issues/1272 +} + +void IRsend::sendSharp2(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + sendDenon(aAddress, aCommand, aNumberOfRepeats, 2); +} + +/* + * Denon frames are always sent 3 times. A non inverted (normal), an inverted frame, ending with a normal frame. + * Repeats are done by just adding an inverted and a normal frame with no extra delay, so it is quite responsible :-) + * If you specify a repeat of e.g. 3, then 3 + 6 frames are sent. + * Measured at Denon RC 1081. + */ +void IRsend::sendDenon(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint8_t aSendSharpFrameMarker) { + // Set IR carrier frequency + enableIROut (DENON_KHZ); // 38 kHz + + // Add frame marker for sharp + uint16_t tCommand = aCommand; + // see https://github.com/Arduino-IRremote/Arduino-IRremote/issues/1272 + tCommand |= aSendSharpFrameMarker << 8; // the 2 upper bits are 00 for Denon and 01 or 10 for Sharp + + uint16_t tData = aAddress | ((uint16_t) tCommand << DENON_ADDRESS_BITS); + uint16_t tInvertedData = (tData ^ 0x7FE0); // Command and frame (upper 10 bits) are inverted + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + + // Data + sendPulseDistanceWidthData_P(&DenonProtocolConstants, tData, DENON_BITS); + + // Inverted autorepeat frame + delay(DENON_AUTO_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI); + sendPulseDistanceWidthData_P(&DenonProtocolConstants, tInvertedData, DENON_BITS); + + tNumberOfCommands--; + // send repeated command with a fixed space gap + delay( DENON_AUTO_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI); + } + /* + * always end with a normal frame + * skip last delay! + */ + sendPulseDistanceWidthData_P(&DenonProtocolConstants, tData, DENON_BITS); + +} + +bool IRrecv::decodeSharp() { + return decodeDenon(); +} + +bool IRrecv::decodeDenon() { + + // we have no start bit, so check for the exact amount of data bits + // Check we have the right amount of data (32). The + 2 is for initial gap + stop bit mark + if (decodedIRData.rawlen != (2 * DENON_BITS) + 2) { + IR_DEBUG_PRINT(F("Denon: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not 32")); + return false; + } + + // Try to decode as Denon protocol + if (!decodePulseDistanceWidthData_P(&DenonProtocolConstants, DENON_BITS, 1)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Denon: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } + + // Check for stop mark + if (!matchMark(decodedIRData.rawDataPtr->rawbuf[(2 * DENON_BITS) + 1], DENON_HEADER_MARK)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Denon: ")); + Serial.println(F("Stop bit mark length is wrong")); +#endif + return false; + } + + // Success + decodedIRData.address = decodedIRData.decodedRawData & 0x1F; + decodedIRData.command = decodedIRData.decodedRawData >> DENON_ADDRESS_BITS; + uint8_t tFrameBits = (decodedIRData.command >> 8) & 0x03; + decodedIRData.command &= 0xFF; + + // Check for (auto) repeat + if (decodedIRData.initialGapTicks < ((DENON_AUTO_REPEAT_DISTANCE + (DENON_AUTO_REPEAT_DISTANCE / 4)) / MICROS_PER_TICK)) { + repeatCount++; + if (repeatCount > 1) { // skip first auto repeat + decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT; + } + + if (tFrameBits & 0x01) { + /* + * Here we are in the auto repeated frame with the inverted command + */ +#if defined(LOCAL_DEBUG) + Serial.print(F("Denon: ")); + Serial.println(F("Autorepeat received=")); +#endif + decodedIRData.flags |= IRDATA_FLAGS_IS_AUTO_REPEAT; + // Check parity of consecutive received commands. There is no parity in one data set. + if ((uint8_t) lastDecodedCommand != (uint8_t)(~decodedIRData.command)) { + decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED; +#if defined(LOCAL_DEBUG) + Serial.print(F("Denon: ")); + Serial.print(F("Parity check for repeat failed. Last command=")); + Serial.print(lastDecodedCommand, HEX); + Serial.print(F(" current=")); + Serial.println(~decodedIRData.command, HEX); +#endif + } + // always take non inverted command + decodedIRData.command = lastDecodedCommand; + } + + // repeated command here + if (tFrameBits == 1 || tFrameBits == 2) { + decodedIRData.protocol = SHARP; + } else { + decodedIRData.protocol = DENON; + } + } else { + repeatCount = 0; + // first command here + if (tFrameBits == 2) { + decodedIRData.protocol = SHARP; + } else { + decodedIRData.protocol = DENON; + } + } + + decodedIRData.numberOfBits = DENON_BITS; + + return true; +} + +/********************************************************************************* + * Old deprecated functions, kept for backward compatibility to old 2.0 tutorials + *********************************************************************************/ +/* + * Only for backwards compatibility + */ +void IRsend::sendDenonRaw(uint16_t aRawData, int_fast8_t aNumberOfRepeats) { + sendDenon(aRawData >> (DENON_COMMAND_BITS + DENON_FRAME_BITS), (aRawData >> DENON_FRAME_BITS) & 0xFF, aNumberOfRepeats); +} + +/* + * Old function with parameter data + */ +void IRsend::sendDenon(unsigned long data, int nbits) { + // Set IR carrier frequency + enableIROut (DENON_KHZ); +#if !(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)) +// Serial.println( +// "The function sendDenon(data, nbits) is deprecated and may not work as expected! Use sendDenonRaw(data, NumberOfRepeats) or better sendDenon(Address, Command, NumberOfRepeats)."); +#endif + + // Header + mark(DENON_HEADER_MARK); + space(DENON_HEADER_SPACE); + + // Data + sendPulseDistanceWidthData(DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, data, nbits, + PROTOCOL_IS_MSB_FIRST); +} + +/* + * Old function without parameter aNumberOfRepeats + */ +void IRsend::sendSharp(uint16_t aAddress, uint16_t aCommand) { + sendDenon(aAddress, aCommand, 0, true); +} + +bool IRrecv::decodeDenonOld(decode_results *aResults) { + + // Check we have the right amount of data + if (decodedIRData.rawlen != 1 + 2 + (2 * DENON_BITS) + 1) { + return false; + } + + // Check initial Mark+Space match + if (!matchMark(aResults->rawbuf[1], DENON_HEADER_MARK)) { + return false; + } + + if (!matchSpace(aResults->rawbuf[2], DENON_HEADER_SPACE)) { + return false; + } + + // Try to decode as Denon protocol. + if (!decodePulseDistanceWidthData(DENON_BITS, 3, DENON_BIT_MARK, DENON_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST)) { + return false; + } + + // Success + aResults->value = decodedIRData.decodedRawData; + aResults->bits = DENON_BITS; + aResults->decode_type = DENON; + decodedIRData.protocol = DENON; + return true; +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_DENON_HPP diff --git a/src/ir_Dish.cpp b/src/ir_Dish.cpp deleted file mode 100644 index 26a88f802..000000000 --- a/src/ir_Dish.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "IRremote.h" - -//============================================================================== -// DDDD IIIII SSSS H H -// D D I S H H -// D D I SSS HHHHH -// D D I S H H -// DDDD IIIII SSSS H H -//============================================================================== - -// Sharp and DISH support by Todd Treece ( http://unionbridge.org/design/ircommand ) -// -// The send function needs to be repeated 4 times -// -// Only send the last for characters of the hex. -// I.E. Use 0x1C10 instead of 0x0000000000001C10 as listed in the LIRC file. -// -// Here is the LIRC file I found that seems to match the remote codes from the -// oscilloscope: -// DISH NETWORK (echostar 301): -// http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx - -#define DISH_BITS 16 -#define DISH_HEADER_MARK 400 -#define DISH_HEADER_SPACE 6100 -#define DISH_BIT_MARK 400 -#define DISH_ONE_SPACE 1700 -#define DISH_ZERO_SPACE 2800 -#define DISH_REPEAT_SPACE 6200 - -//+============================================================================= -#if SEND_DISH -void IRsend::sendDISH(unsigned long data, int nbits) { - // Set IR carrier frequency - enableIROut(56); - - mark(DISH_HEADER_MARK); - space(DISH_HEADER_SPACE); - - sendPulseDistanceWidthData(DISH_BIT_MARK, DISH_ONE_SPACE, DISH_BIT_MARK, DISH_ZERO_SPACE, data, nbits); -// for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) { -// if (data & mask) { -// mark(DISH_BIT_MARK); -// space(DISH_ONE_SPACE); -// } else { -// mark(DISH_BIT_MARK); -// space(DISH_ZERO_SPACE); -// } -// } - - mark(DISH_HEADER_MARK); //added 26th March 2016, by AnalysIR ( https://www.AnalysIR.com ) - space(0); // Always end with the LED off -} -#endif - diff --git a/src/ir_DistanceWidthProtocol.hpp b/src/ir_DistanceWidthProtocol.hpp new file mode 100644 index 000000000..bea846768 --- /dev/null +++ b/src/ir_DistanceWidthProtocol.hpp @@ -0,0 +1,486 @@ +/* + * ir_DistanceWidthProtocol.hpp + * + * Contains only the decoder functions for universal pulse width or pulse distance protocols! + * The send functions are used by almost all protocols and are therefore located in IRSend.hpp. + * + * If RAM is not more than 2k, the decoder only accepts mark or space durations up to 50 * 50 (MICROS_PER_TICK) = 2500 microseconds + * to save RAM space, otherwise it accepts durations up to 10 ms. + * + * This decoder tries to decode a pulse distance or pulse distance width with constant period (or pulse width - not enabled yet) protocol. + * 1. Analyze all space and mark length + * 2. Decide which protocol we have + * 3. Try to decode with the mark and space data found in step 1 + * 4. Assume one start bit / header and one stop bit, since pulse distance data must have a stop bit! + * No data and address decoding, only raw data as result. + * + * Pulse distance data can be sent with the generic function as in SendDemo example line 155: + * https://github.com/Arduino-IRremote/Arduino-IRremote/blob/d51b540cb2ddf1424888d2d9e6b62fe1ef46859d/examples/SendDemo/SendDemo.ino#L155 + * void sendPulseDistanceWidthData(unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros, + * unsigned int aZeroSpaceMicros, uint32_t aData, uint8_t aNumberOfBits, bool aMSBfirst, bool aSendStopBit = false) + * The header must be sent manually with: + * IrSender.mark(MarkMicros) + * IrSender.space(SpaceMicros); + * + * Or send it by filling a DecodedRawDataArray and with the sendPulseDistanceWidthFromArray() function as in SendDemo example line 175: + * https://github.com/Arduino-IRremote/Arduino-IRremote/blob/d51b540cb2ddf1424888d2d9e6b62fe1ef46859d/examples/SendDemo/SendDemo.ino#L175 + * sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, unsigned int aHeaderMarkMicros, + * unsigned int aHeaderSpaceMicros, unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros, + * unsigned int aZeroSpaceMicros, uint32_t *aDecodedRawDataArray, unsigned int aNumberOfBits, uint8_t aFlags, + * unsigned int aRepeatPeriodMillis, int_fast8_t aNumberOfRepeats) + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2022-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_DISTANCE_WIDTH_HPP +#define _IR_DISTANCE_WIDTH_HPP + +#if !defined(DISTANCE_WIDTH_MAXIMUM_REPEAT_DISTANCE_MICROS) +#define DISTANCE_WIDTH_MAXIMUM_REPEAT_DISTANCE_MICROS 100000 // 100 ms, bit it is just a guess +#endif + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +#if !defined(DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE) +# if (defined(RAMEND) && RAMEND <= 0x8FF) || (defined(RAMSIZE) && RAMSIZE < 0x8FF) +#define DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE 50 // To save program space, the decoder only accepts mark or space durations up to 50 * 50 (MICROS_PER_TICK) = 2500 microseconds +# else +#define DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE 200 // The decoder accepts mark or space durations up to 200 * 50 (MICROS_PER_TICK) = 10 milliseconds +# endif +#endif + +// Switch the decoding according to your needs +//#define USE_MSB_DECODING_FOR_DISTANCE_DECODER // If active, it resembles LG, otherwise LSB first as most other protocols e.g. NEC and Kaseikyo/Panasonic + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +//===================================================================================== +// DDD III SSS TTTTTT AA N N CCC EEEE W W III DDD TTTTTT H H +// D D I S TT A A NN N C E W W I D D TT H H +// D D I SSS TT AAAA N N N C EEE W W W I D D TT HHHH +// D D I S TT A A N NN C E W W W I D D TT H H +// DDD III SSSS TT A A N N CCC EEEE W W III DDD TT H H +//===================================================================================== +// see: https://www.mikrocontroller.net/articles/IRMP_-_english#Codings +/* + Example output of UnitTest.ino for PulseWidth protocol: + Protocol=PulseWidth Raw-Data=0x87654321 32 bits LSB first + Send on a 8 bit platform with: IrSender.sendPulseDistanceWidth(38, 950, 550, 600, 300, 300, 300, 0x87654321, 32, PROTOCOL_IS_LSB_FIRST, , ); + rawData[66]: + -1088600 + + 950,- 550 + + 600,- 300 + 300,- 300 + 350,- 250 + 350,- 250 + + 350,- 300 + 600,- 300 + 300,- 300 + 300,- 300 + + 650,- 250 + 650,- 250 + 300,- 300 + 350,- 250 + + 350,- 300 + 300,- 300 + 600,- 300 + 300,- 300 + + 600,- 300 + 350,- 250 + 600,- 300 + 350,- 250 + + 350,- 300 + 600,- 300 + 600,- 300 + 300,- 300 + + 600,- 300 + 600,- 300 + 600,- 300 + 300,- 300 + + 300,- 300 + 300,- 300 + 350,- 250 + 650 + Sum: 24500 + + Example output of UnitTest.ino for PulseDistanceWidth protocol: + Protocol=PulseDistance Raw-Data=0x76 7 bits LSB first + Send on a 8 bit platform with: IrSender.sendPulseDistanceWidth(38, 5950, 500, 550, 1450, 1550, 500, 0x76, 7, PROTOCOL_IS_LSB_FIRST, , ); + rawData[18]: + -1092450 + +5950,- 500 + +1500,- 500 + 500,-1450 + 550,-1450 +1550,- 450 + + 550,-1450 + 550,-1450 + 550,-1450 + 550 + Sum: 20950 + */ + +#if defined(LOCAL_DEBUG) +void printDurations(uint8_t aArray[], uint8_t aMaxIndex) { + for (uint_fast8_t i = 0; i <= aMaxIndex; i++) { + //Print index at the beginning of a new line + if (i % 10 == 0) { + if (i == 0) { + Serial.print(' '); // indentation for the first index 0 + } else { + Serial.println(); // new line for next indexes 10, 20 etc. + } + Serial.print(i); + Serial.print(F(": ")); + } + // Print number of values in array and duration if != 0 + Serial.print(aArray[i]); + if (aArray[i] != 0) { + Serial.print('x'); + Serial.print(i * (uint16_t) MICROS_PER_TICK); + } + Serial.print(F(" | ")); + } + Serial.println(); +} +#endif + +/* + * We count all consecutive (allow only one gap between) durations and compute the average. + * @return false if more than 2 distinct duration values found + */ +bool aggregateArrayCounts(uint8_t aArray[], uint8_t aMaxIndex, uint8_t *aShortIndex, uint8_t *aLongIndex) { + uint8_t tSum = 0; + uint16_t tWeightedSum = 0; + uint8_t tGapCount = 0; + for (uint_fast8_t i = 0; i <= aMaxIndex; i++) { + uint8_t tCurrentDurations = aArray[i]; + if (tCurrentDurations != 0) { + // Add it to sum and remove array content + tSum += tCurrentDurations; + tWeightedSum += (tCurrentDurations * i); + aArray[i] = 0; + tGapCount = 0; + } else { + tGapCount++; + } + if (tSum != 0 && (i == aMaxIndex || tGapCount > 1)) { + /* + * Here we have a sum AND last element OR more than 1 consecutive gap + */ + uint8_t tAggregateIndex = (tWeightedSum + (tSum / 2)) / tSum; // with rounding + aArray[tAggregateIndex] = tSum; // disabling this line increases code size by 2 - unbelievable! + // store aggregate for later decoding + if (*aShortIndex == 0) { + *aShortIndex = tAggregateIndex; + } else if (*aLongIndex == 0) { + *aLongIndex = tAggregateIndex; + } else { + // we have 3 bins => this is likely no pulse width or distance protocol. e.g. it can be RC5. + return false; + } + // initialize for next aggregation + tSum = 0; + tWeightedSum = 0; + } + } + return true; +} + +/* + * Try to decode a pulse distance or pulse width protocol. + * 1. Analyze all space and mark length + * 2. Decide if we have an pulse width or distance protocol + * 3. Try to decode with the mark and space data found in step 1 + * No data and address decoding, only raw data as result. + * + * calloc() version is 700 bytes larger :-( + */ +bool IRrecv::decodeDistanceWidth() { + /* + * Array for up to 49 ticks / 2500 us (or 199 ticks / 10 ms us if RAM > 2k) + * 0 tick covers mark or space durations from 0 to 49 us, and 49 ticks from 2450 to 2499 us + */ + uint8_t tDurationArray[DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE]; + + /* + * Accept only protocols with at least 7 bits + */ + if (decodedIRData.rawlen < (2 * 7) + 4) { + IR_DEBUG_PRINT(F("PULSE_DISTANCE_WIDTH: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is less than 18")); + return false; + } + + // Reset duration array + memset(tDurationArray, 0, DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE); + + uint8_t tIndexOfMaxDuration = 0; + /* + * Count number of mark durations. Skip leading start and trailing stop bit. + */ + for (IRRawlenType i = 3; i < decodedIRData.rawlen - 2; i += 2) { + auto tDurationTicks = decodedIRData.rawDataPtr->rawbuf[i]; + if (tDurationTicks < DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE) { + tDurationArray[tDurationTicks]++; // count duration if less than DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE + if (tIndexOfMaxDuration < tDurationTicks) { + tIndexOfMaxDuration = tDurationTicks; + } + } else { +#if defined(LOCAL_DEBUG) + Serial.print(F("PULSE_DISTANCE_WIDTH: ")); + Serial.print(F("Mark ")); + Serial.print(tDurationTicks * MICROS_PER_TICK); + Serial.print(F(" is longer than maximum ")); + Serial.print(DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE * MICROS_PER_TICK); + Serial.print(F(" us. Index=")); + Serial.println(i); +#endif + return false; + } + } + + /* + * Aggregate mark counts to one duration bin + */ + uint8_t tMarkTicksShort = 0; + uint8_t tMarkTicksLong = 0; + bool tSuccess = aggregateArrayCounts(tDurationArray, tIndexOfMaxDuration, &tMarkTicksShort, &tMarkTicksLong); +#if defined(LOCAL_DEBUG) + Serial.println(F("Mark:")); + printDurations(tDurationArray, tIndexOfMaxDuration); +#endif + + if (!tSuccess) { +#if defined(LOCAL_DEBUG) + Serial.print(F("PULSE_DISTANCE_WIDTH: ")); + Serial.println(F("Mark aggregation failed, more than 2 distinct mark duration values found")); +#endif + return false; + } + + // Reset duration array + memset(tDurationArray, 0, DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE); + + /* + * Count number of space durations. Skip leading start and trailing stop bit. + */ + tIndexOfMaxDuration = 0; + for (IRRawlenType i = 4; i < decodedIRData.rawlen - 2; i += 2) { + auto tDurationTicks = decodedIRData.rawDataPtr->rawbuf[i]; + if (tDurationTicks < DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE) { + tDurationArray[tDurationTicks]++; + if (tIndexOfMaxDuration < tDurationTicks) { + tIndexOfMaxDuration = tDurationTicks; + } + } else { +#if defined(LOCAL_DEBUG) + Serial.print(F("PULSE_DISTANCE_WIDTH: ")); + Serial.print(F("Space ")); + Serial.print(tDurationTicks * MICROS_PER_TICK); + Serial.print(F(" is longer than maximum ")); + Serial.print(DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE * MICROS_PER_TICK); + Serial.print(F(" us. Index=")); + Serial.println(i); +#endif + return false; + } + } + + /* + * Aggregate space counts to one duration bin + */ + uint8_t tSpaceTicksShort = 0; + uint8_t tSpaceTicksLong = 0; + tSuccess = aggregateArrayCounts(tDurationArray, tIndexOfMaxDuration, &tSpaceTicksShort, &tSpaceTicksLong); +#if defined(LOCAL_DEBUG) + Serial.println(F("Space:")); + printDurations(tDurationArray, tIndexOfMaxDuration); +#endif + + if (!tSuccess) { +#if defined(LOCAL_DEBUG) + Serial.print(F("PULSE_DISTANCE_WIDTH: ")); + Serial.println(F("Space aggregation failed, more than 2 distinct space duration values found")); +#endif + return false; + } + + /* + * Print characteristics of this protocol. Durations are in (50 us) ticks. + * Number of bits, start bit, start pause, long mark, long space, short mark, short space + * + * NEC: 32, 180, 90, 0, 34, 11, 11 + * Samsung32: 32, 90, 90, 0, 34, 11, 11 + * LG: 28, 180, 84, 0, 32, 10, 11 + * JVC: 16, 168, 84, 0, 32, 10, 10 + * Kaseikyo: 48. 69, 35, 0, 26, 9, 9 + * Sony: 12|15|20, 48, 12, 24, 0, 12, 12 // the only known pulse width protocol + * Disney monorail + * model: 7, 120, 10, 30, 30, 10, 10 // PulseDistanceWidth. Can be seen as direct conversion of a 7 bit serial timing at 250 baud with a 6 ms start bit. + */ +#if defined(LOCAL_DEBUG) + Serial.print(F("DistanceWidthTimingInfoStruct: ")); + Serial.print(decodedIRData.rawDataPtr->rawbuf[1] * MICROS_PER_TICK); + Serial.print(F(", ")); + Serial.print(decodedIRData.rawDataPtr->rawbuf[2] * MICROS_PER_TICK); + Serial.print(F(", ")); + Serial.print(tMarkTicksLong * MICROS_PER_TICK); + Serial.print(F(", ")); + Serial.print(tSpaceTicksLong * MICROS_PER_TICK); + Serial.print(F(", ")); + Serial.print(tMarkTicksShort * MICROS_PER_TICK); + Serial.print(F(", ")); + Serial.println(tSpaceTicksShort * MICROS_PER_TICK); +#endif +#if RAW_BUFFER_LENGTH <= (512 -4) + uint_fast8_t tNumberOfBits; +#else + uint16_t tNumberOfBits; +#endif + tNumberOfBits = (decodedIRData.rawlen / 2) - 1; + if (tSpaceTicksLong > 0) { + // For PULSE_DISTANCE -including PULSE_DISTANCE_WIDTH- a stop bit is mandatory, for PULSE_WIDTH it is not required! + tNumberOfBits--; // Correct for stop bit + } + decodedIRData.numberOfBits = tNumberOfBits; + uint8_t tNumberOfAdditionalArrayValues = (tNumberOfBits - 1) / BITS_IN_RAW_DATA_TYPE; + + /* + * We can have the following protocol timings + * PULSE_DISTANCE: Pause/spaces have different length and determine the bit value, longer space is 1. Pulses/marks can be constant, like NEC. + * PULSE_WIDTH: Pulses/marks have different length and determine the bit value, longer mark is 1. Pause/spaces can be constant, like Sony. + * PULSE_DISTANCE_WIDTH: Pulses/marks and pause/spaces have different length, often the bit length is constant, like MagiQuest. Can be decoded by PULSE_DISTANCE decoder. + */ + + if (tMarkTicksLong == 0 && tSpaceTicksLong == 0) { +#if defined(LOCAL_DEBUG) + Serial.print(F("PULSE_DISTANCE: ")); + Serial.println(F("Only 1 distinct duration value for each space and mark found")); +#endif + return false; + } + unsigned int tSpaceMicrosShort; +#if defined DECODE_STRICT_CHECKS + if(tMarkTicksLong > 0 && tSpaceTicksLong > 0) { + // We have different mark and space length here, so signal decodePulseDistanceWidthData() not to check against constant length decodePulseDistanceWidthData + tSpaceMicrosShort = 0; + } +#endif + tSpaceMicrosShort = tSpaceTicksShort * MICROS_PER_TICK; + unsigned int tMarkMicrosShort = tMarkTicksShort * MICROS_PER_TICK; + unsigned int tMarkMicrosLong = tMarkTicksLong * MICROS_PER_TICK; + unsigned int tSpaceMicrosLong = tSpaceTicksLong * MICROS_PER_TICK; + IRRawlenType tStartIndex = 3; // skip leading start bit for decoding. + + for (uint_fast8_t i = 0; i <= tNumberOfAdditionalArrayValues; ++i) { + uint8_t tNumberOfBitsForOneDecode = tNumberOfBits; + /* + * Decode in 32/64 bit chunks. Only the last chunk can contain less than 32/64 bits + */ + if (tNumberOfBitsForOneDecode > BITS_IN_RAW_DATA_TYPE) { + tNumberOfBitsForOneDecode = BITS_IN_RAW_DATA_TYPE; + } + bool tResult; + if (tSpaceTicksLong > 0) { + /* + * Here short and long space durations found. + * Since parameters aOneMarkMicros and aOneSpaceMicros are equal, we only check tSpaceMicrosLong here. + */ + decodedIRData.protocol = PULSE_DISTANCE; // Sony + PULSE_DISTANCE_WIDTH + tResult = decodePulseDistanceWidthData(tNumberOfBitsForOneDecode, tStartIndex, tMarkMicrosShort, tSpaceMicrosLong, + tMarkMicrosShort, +#if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER) + true +#else + false +#endif + ); + } else { + /* + * Here no long space duration found. => short and long mark durations found, check tMarkMicrosLong here + * This else case will most likely never be used, but it only requires 12 bytes additional programming space :-) + */ + decodedIRData.protocol = PULSE_WIDTH; // NEC etc. + tResult = decodePulseDistanceWidthData(tNumberOfBitsForOneDecode, tStartIndex, tMarkMicrosLong, tSpaceMicrosShort, + tMarkMicrosShort, +#if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER) + true +#else + false +#endif + ); + + } + if (!tResult) { +#if defined(LOCAL_DEBUG) + Serial.print(F("PULSE_WIDTH: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } +#if defined(LOCAL_DEBUG) + Serial.print(F("PULSE_WIDTH: ")); + Serial.print(F("decodedRawData=0x")); + Serial.println(decodedIRData.decodedRawData, HEX); +#endif + // fill array with decoded data + decodedIRData.decodedRawDataArray[i] = decodedIRData.decodedRawData; + tStartIndex += (2 * BITS_IN_RAW_DATA_TYPE); + tNumberOfBits -= BITS_IN_RAW_DATA_TYPE; + } + +#if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER) + decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; +#endif + + // Check for repeat. Check also for equality of last DecodedRawData. + if (decodedIRData.initialGapTicks < DISTANCE_WIDTH_MAXIMUM_REPEAT_DISTANCE_MICROS / MICROS_PER_TICK + && decodedIRData.decodedRawDataArray[tNumberOfAdditionalArrayValues] == lastDecodedRawData) { + decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT; + } + lastDecodedRawData = decodedIRData.decodedRawData; + + /* + * Store timing data to reproduce frame for sending + */ + decodedIRData.DistanceWidthTimingInfo.HeaderMarkMicros = (decodedIRData.rawDataPtr->rawbuf[1] * MICROS_PER_TICK); + decodedIRData.DistanceWidthTimingInfo.HeaderSpaceMicros = (decodedIRData.rawDataPtr->rawbuf[2] * MICROS_PER_TICK); + decodedIRData.DistanceWidthTimingInfo.ZeroMarkMicros = tMarkMicrosShort; + decodedIRData.DistanceWidthTimingInfo.ZeroSpaceMicros = tSpaceMicrosShort; + if (tMarkMicrosLong != 0) { + if (tSpaceMicrosLong == 0) { + // PULSE_DISTANCE, Sony + decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosLong; + decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosShort; + } else { + // PULSE_DISTANCE_WIDTH, we have 4 distinct values here + // Assume long space for a one when we have PulseDistanceWidth like for RS232, where a long inactive period (high) is a 1 + decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosLong; + decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosShort; + decodedIRData.DistanceWidthTimingInfo.ZeroMarkMicros = tMarkMicrosLong; +// // Assume long mark for a one when we have PulseDistanceWidth +// decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosShort; +// decodedIRData.DistanceWidthTimingInfo.ZeroSpaceMicros = tSpaceMicrosLong; +// decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosLong; + } + } else { + // PULSE_WIDTH, NEC etc. + // Here tMarkMicrosLong is 0 => tSpaceMicrosLong != 0 + decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosShort; + decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosLong; + } + +#if defined(LOCAL_DEBUG) + Serial.print(F("DistanceWidthTimingInfo=")); + IrReceiver.printDistanceWidthTimingInfo(&Serial, &decodedIRData.DistanceWidthTimingInfo); + Serial.println(); +#endif + return true; +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_DISTANCE_WIDTH_HPP diff --git a/src/ir_FAST.hpp b/src/ir_FAST.hpp new file mode 100644 index 000000000..e01f98296 --- /dev/null +++ b/src/ir_FAST.hpp @@ -0,0 +1,156 @@ +/* + * ir_FAST.hpp + * + * Contains functions for receiving and sending FAST IR protocol + * with no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2023 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_FAST_HPP +#define _IR_FAST_HPP + +#include "TinyIR.h" + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +// generated with https://patorjk.com/software/taag/#p=display&f=Alphabet&t=FAST +//============================================================================== +// FFFF AA SSS TTTTTT +// F A A S TT +// FFF AAAA SSS TT +// F A A S TT +// F A A SSSS TT +//============================================================================== +#include "TinyIR.h" +/* + Protocol=FAST Address=0x0 Command=0x76 Raw-Data=0x8976 16 bits LSB first + +2100,-1050 + + 550,- 500 + 550,-1550 + 550,-1550 + 550,- 500 + + 550,-1550 + 550,-1550 + 550,-1550 + 550,- 500 + + 550,-1550 + 550,- 500 + 550,- 500 + 550,-1550 + + 550,- 500 + 550,- 500 + 550,- 500 + 550,-1550 + + 550 + Sum: 28900 + */ +struct PulseDistanceWidthProtocolConstants const FASTProtocolConstants PROGMEM = { FAST, FAST_KHZ, FAST_HEADER_MARK, + FAST_HEADER_SPACE, + FAST_BIT_MARK, FAST_ONE_SPACE, FAST_BIT_MARK, FAST_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (FAST_REPEAT_PERIOD + / MICROS_IN_ONE_MILLI), nullptr }; + +/************************************ + * Start of send and decode functions + ************************************/ + +/** + * The FAST protocol repeats by skipping the header mark and space -> this leads to a poor repeat detection for JVC protocol. + */ +void IRsend::sendFAST(uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + // Set IR carrier frequency + enableIROut(FAST_KHZ); // 38 kHz + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + + mark(FAST_HEADER_MARK); + space(FAST_HEADER_SPACE); + + sendPulseDistanceWidthData_P(&FASTProtocolConstants, aCommand | (((uint8_t)(~aCommand)) << 8), FAST_BITS); + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + // send repeated command in a fixed raster + delay(FAST_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI); + } + } +} + +bool IRrecv::decodeFAST() { + +// uint_fast8_t tRawlen = decodedIRData.rawlen; // Using a local variable does not improve code size + + // Check we have the right amount of data (36). The +4 is for initial gap, start bit mark and space + stop bit mark. + if (decodedIRData.rawlen != ((2 * FAST_BITS) + 4)) { + IR_DEBUG_PRINT(F("FAST: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not 36")); + return false; + } + + if (!checkHeader_P(&FASTProtocolConstants)) { + return false; + } + + if (!decodePulseDistanceWidthData_P(&FASTProtocolConstants, FAST_BITS)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("FAST: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } + + WordUnion tValue; + tValue.UWord = decodedIRData.decodedRawData; + + if (tValue.UByte.LowByte != (uint8_t) ~(tValue.UByte.HighByte)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("FAST: ")); + Serial.print(F("8 bit parity is not correct. Expected=0x")); + Serial.print((uint8_t)~(tValue.UByte.LowByte), HEX); + Serial.print(F(" received=0x")); + Serial.print(tValue.UByte.HighByte, HEX); + Serial.print(F(" data=0x")); + Serial.println(tValue.UWord, HEX); +#endif + decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED; + } + + checkForRepeatSpaceTicksAndSetFlag(FAST_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK); + + // Success +// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value + decodedIRData.command = tValue.UByte.LowByte; + decodedIRData.address = 0; // No address for this protocol + decodedIRData.numberOfBits = FAST_BITS; + decodedIRData.protocol = FAST; + + return true; +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_FAST_HPP diff --git a/src/ir_JVC.cpp b/src/ir_JVC.cpp deleted file mode 100644 index a1d8880ac..000000000 --- a/src/ir_JVC.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "IRremote.h" - -//============================================================================== -// JJJJJ V V CCCC -// J V V C -// J V V C -// J J V V C -// J V CCCC -//============================================================================== - -// https://www.sbprojects.net/knowledge/ir/jvc.php -// Here it is decoded MSB first!!! It is not corrected in order to be backwards compatible! - -#define JVC_BITS 16 -#define JVC_HEADER_MARK 8400 -#define JVC_HEADER_SPACE 4200 -#define JVC_BIT_MARK 600 -#define JVC_ONE_SPACE 1600 -#define JVC_ZERO_SPACE 550 -#define JVC_REPEAT_SPACE 50000 // Not used. - -//+============================================================================= -// JVC does NOT repeat by sending a separate code (like NEC does). -// The JVC protocol repeats by skipping the header. -// To send a JVC repeat signal, send the original code value -// and set 'repeat' to true -// -// JVC commands sometimes need to be sent two or three times with 40 to 60 ms pause in between. -// -#if SEND_JVC -void IRsend::sendJVC(unsigned long data, int nbits, bool repeat) { - // Set IR carrier frequency - enableIROut(38); - - // Only send the Header if this is NOT a repeat command - if (!repeat) { - mark(JVC_HEADER_MARK); - space(JVC_HEADER_SPACE); - } - - // Data - sendPulseDistanceWidthData(JVC_BIT_MARK, JVC_ONE_SPACE, JVC_BIT_MARK, JVC_ZERO_SPACE, data, nbits, true); - -// Footer - mark(JVC_BIT_MARK); - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -#if DECODE_JVC -bool IRrecv::decodeJVC() { - int offset = 1; // Skip first space - - // Check for repeat - if ((results.rawlen - 1 == 33) && MATCH_MARK(results.rawbuf[offset], JVC_BIT_MARK) - && MATCH_MARK(results.rawbuf[results.rawlen - 1], JVC_BIT_MARK)) { - results.bits = 0; - results.value = REPEAT; - results.isRepeat = true; - results.decode_type = JVC; - return true; - } - - // Initial mark - if (!MATCH_MARK(results.rawbuf[offset], JVC_HEADER_MARK)) { - return false; - } - offset++; - - // Check we have enough data - +3 for start bit mark and space + stop bit mark - if (results.rawlen <= (2 * JVC_BITS) + 3) { - return false; - } - - // Initial space - if (!MATCH_SPACE(results.rawbuf[offset], JVC_HEADER_SPACE)) { - return false; - } - offset++; - - if (!decodePulseDistanceData(JVC_BITS, offset, JVC_BIT_MARK, JVC_ONE_SPACE, JVC_ZERO_SPACE)) { - return false; - } - - // Stop bit - if (!MATCH_MARK(results.rawbuf[offset + (2 * JVC_BITS)], JVC_BIT_MARK)) { - DBG_PRINT("Stop bit verify failed"); - return false; - } - - // Success - results.bits = JVC_BITS; - results.decode_type = JVC; - - return true; -} - -bool IRrecv::decodeJVC(decode_results *aResults) { - bool aReturnValue = decodeJVC(); - *aResults = results; - return aReturnValue; -} -#endif - diff --git a/src/ir_JVC.hpp b/src/ir_JVC.hpp new file mode 100644 index 000000000..ef69a5986 --- /dev/null +++ b/src/ir_JVC.hpp @@ -0,0 +1,261 @@ +/* + * ir_JVC.hpp + * + * Contains functions for receiving and sending JVC IR Protocol in "raw" and standard format with 8 bit address and 8 bit command + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2017-2023 Kristian Lauszus, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_JVC_HPP +#define _IR_JVC_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +//============================================================================== +// JJJJJ V V CCCC +// J V V C +// J V V C +// J J V V C +// J V CCCC +//============================================================================== +/* + +8400,-4150 + + 550,-1550 + 550,- 500 + 550,- 500 + 550,- 500 + + 550,-1500 + 600,-1500 + 600,-1500 + 550,-1550 + + 550,- 500 + 550,-1550 + 550,-1550 + 550,- 500 + + 500,-1600 + 550,-1550 + 550,-1500 + 600,- 500 + + 550 + Sum: 40350 + */ +// https://www.sbprojects.net/knowledge/ir/jvc.php +// http://www.hifi-remote.com/johnsfine/DecodeIR.html#JVC +// IRP: {38k,525}<1,-1|1,-3>(16,-8,(D:8,F:8,1,-45)+) +// LSB first, 1 start bit + 8 bit address + 8 bit command + 1 stop bit. +// The JVC protocol repeats by skipping the header mark and space -> this leads to a poor repeat detection for JVC protocol. +// Some JVC devices require to send 3 repeats. + +#define JVC_ADDRESS_BITS 8 // 8 bit address +#define JVC_COMMAND_BITS 8 // Command + +#define JVC_BITS (JVC_ADDRESS_BITS + JVC_COMMAND_BITS) // 16 - The number of bits in the protocol +#define JVC_UNIT 526 // 20 periods of 38 kHz (526.315789) + +#define JVC_HEADER_MARK (16 * JVC_UNIT) // 8400 +#define JVC_HEADER_SPACE (8 * JVC_UNIT) // 4200 + +#define JVC_BIT_MARK JVC_UNIT // The length of a Bit:Mark +#define JVC_ONE_SPACE (3 * JVC_UNIT) // 1578 - The length of a Bit:Space for 1's +#define JVC_ZERO_SPACE JVC_UNIT // The length of a Bit:Space for 0's + +#define JVC_REPEAT_DISTANCE (uint16_t)(45 * JVC_UNIT) // 23625 - Commands are repeated with a distance of 23 ms for as long as the key on the remote control is held down. +#define JVC_REPEAT_PERIOD 65000 // assume around 40 ms for a JVC frame. JVC IR Remotes: RM-SA911U, RM-SX463U have 45 ms period + +struct PulseDistanceWidthProtocolConstants const JVCProtocolConstants PROGMEM = {JVC, JVC_KHZ, JVC_HEADER_MARK, JVC_HEADER_SPACE, JVC_BIT_MARK, + JVC_ONE_SPACE, JVC_BIT_MARK, JVC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (JVC_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr}; + +/************************************ + * Start of send and decode functions + ************************************/ + +/** + * The JVC protocol repeats by skipping the header mark and space -> this leads to a poor repeat detection for JVC protocol. + */ +void IRsend::sendJVC(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + // Set IR carrier frequency + enableIROut (JVC_KHZ); // 38 kHz + + if (aNumberOfRepeats < 0) { + // The JVC protocol repeats by skipping the header. + aNumberOfRepeats = 0; + } else { + mark(JVC_HEADER_MARK); + space(JVC_HEADER_SPACE); + } + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + + // Address + command + sendPulseDistanceWidthData_P(&JVCProtocolConstants, aAddress | (aCommand << JVC_ADDRESS_BITS), JVC_BITS); + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + // send repeated command in a fixed raster + delay(JVC_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI); + } + } +} + +bool IRrecv::decodeJVC() { + +// uint_fast8_t tRawlen = decodedIRData.rawlen; // Using a local variable does not improve code size + + // Check we have the right amount of data (36 or 34). The +4 is for initial gap, start bit mark and space + stop bit mark. + // +4 is for first frame, +2 is for repeats + if (decodedIRData.rawlen != ((2 * JVC_BITS) + 2) && decodedIRData.rawlen != ((2 * JVC_BITS) + 4)) { + IR_DEBUG_PRINT(F("JVC: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not 34 or 36")); + return false; + } + + if (decodedIRData.rawlen == ((2 * JVC_BITS) + 2)) { + /* + * Check for repeat + * Check leading space and first and last mark length + */ + if (decodedIRData.initialGapTicks < ((JVC_REPEAT_DISTANCE + (JVC_REPEAT_DISTANCE / 4) / MICROS_PER_TICK)) + && matchMark(decodedIRData.rawDataPtr->rawbuf[1], JVC_BIT_MARK) + && matchMark(decodedIRData.rawDataPtr->rawbuf[decodedIRData.rawlen - 1], JVC_BIT_MARK)) { + /* + * We have a repeat here, so do not check for start bit + */ + decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_LSB_FIRST; + decodedIRData.address = lastDecodedAddress; + decodedIRData.command = lastDecodedCommand; + decodedIRData.protocol = JVC; + } + } else { + + if (!checkHeader_P(&JVCProtocolConstants)) { + return false; + } + + if (!decodePulseDistanceWidthData_P(&JVCProtocolConstants, JVC_BITS)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("JVC: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } + + // Success +// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value + decodedIRData.command = decodedIRData.decodedRawData >> JVC_ADDRESS_BITS; // upper 8 bits of LSB first value + decodedIRData.address = decodedIRData.decodedRawData & 0xFF; // lowest 8 bit of LSB first value + decodedIRData.numberOfBits = JVC_BITS; + decodedIRData.protocol = JVC; + } + + return true; +} + +/********************************************************************************* + * Old deprecated functions, kept for backward compatibility to old 2.0 tutorials + *********************************************************************************/ +bool IRrecv::decodeJVCMSB(decode_results *aResults) { + unsigned int offset = 1; // Skip first space + + // Check for repeat + if ((aResults->rawlen - 1 == 33) && matchMark(aResults->rawbuf[offset], JVC_BIT_MARK) + && matchMark(aResults->rawbuf[aResults->rawlen - 1], JVC_BIT_MARK)) { + aResults->bits = 0; + aResults->value = 0xFFFFFFFF; + decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT; + decodedIRData.protocol = JVC; + return true; + } + + // Initial mark + if (!matchMark(aResults->rawbuf[offset], JVC_HEADER_MARK)) { + return false; + } + offset++; + + // Check we have enough data - +3 for start bit mark and space + stop bit mark + if (aResults->rawlen <= (2 * JVC_BITS) + 3) { + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(aResults->rawlen); + IR_DEBUG_PRINTLN(F(" is too small. >= 36 is required.")); + return false; + } + + // Initial space + if (!matchSpace(aResults->rawbuf[offset], JVC_HEADER_SPACE)) { + return false; + } + offset++; + + if (!decodePulseDistanceWidthData(JVC_BITS, offset, JVC_BIT_MARK, JVC_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST)) { + return false; + } + + // Stop bit + if (!matchMark(aResults->rawbuf[offset + (2 * JVC_BITS)], JVC_BIT_MARK)) { +#if defined(LOCAL_DEBUG) + Serial.println(F("Stop bit mark length is wrong")); +#endif + return false; + } + + // Success + aResults->value = decodedIRData.decodedRawData; + aResults->bits = JVC_BITS; + aResults->decode_type = JVC; + decodedIRData.protocol = JVC; + + return true; +} + +/** + * With Send sendJVCMSB() you can send your old 32 bit codes. + * To convert one into the other, you must reverse the byte positions and then reverse all bit positions of each byte. + * Use bitreverse32Bit(). + * Or write it as one binary string and reverse/mirror it. + * Example: + * 0xCB340102 byte reverse -> 02 01 34 CB bit reverse-> 40 80 2C D3. + * 0xCB340102 is binary 11001011001101000000000100000010. + * 0x40802CD3 is binary 01000000100000000010110011010011. + * If you read the first binary sequence backwards (right to left), you get the second sequence. + */ +void IRsend::sendJVCMSB(unsigned long data, int nbits, bool repeat) { + // Set IR carrier frequency + enableIROut (JVC_KHZ); + + // Only send the Header if this is NOT a repeat command + if (!repeat) { + mark(JVC_HEADER_MARK); + space(JVC_HEADER_SPACE); + } + + // Old version with MSB first Data + sendPulseDistanceWidthData(JVC_BIT_MARK, JVC_ONE_SPACE, JVC_BIT_MARK, JVC_ZERO_SPACE, data, nbits, PROTOCOL_IS_MSB_FIRST); +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_JVC_HPP diff --git a/src/ir_Kaseikyo.hpp b/src/ir_Kaseikyo.hpp new file mode 100644 index 000000000..9abce660c --- /dev/null +++ b/src/ir_Kaseikyo.hpp @@ -0,0 +1,322 @@ +/* + * ir_Kaseikyo.hpp + * + * Contains functions for receiving and sending Kaseikyo/Panasonic IR Protocol in "raw" and standard format with 16 bit address + 8 bit command + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2023 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_KASEIKYO_HPP +#define _IR_KASEIKYO_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +//============================================================================== +// K K AA SSS EEEE III K K Y Y OOO +// K K A A S E I K K Y Y O O +// KK AAAA SSS EEE I KK Y O O +// K K A A S E I K K Y O O +// K K A A SSSS EEEE III K K Y OOO +//============================================================================== +//============================================================================== +// PPPP AAA N N AAA SSSS OOO N N IIIII CCCC +// P P A A NN N A A S O O NN N I C +// PPPP AAAAA N N N AAAAA SSS O O N N N I C +// P A A N NN A A S O O N NN I C +// P A A N N A A SSSS OOO N N IIIII CCCC +//============================================================================== +/* + Protocol=Panasonic Address=0xFF1 Command=0x76 Raw-Data=0x9976FF10 48 bits LSB first + +3450,-1700 + + 450,- 400 + 500,-1250 + 450,- 400 + 500,- 400 + + 450,- 400 + 400,- 450 + 500,- 350 + 450,- 450 + + 450,- 400 + 450,- 400 + 500,- 400 + 450,- 400 + + 450,- 400 + 500,-1250 + 450,- 400 + 500,- 350 + + 500,- 400 + 450,- 400 + 450,- 450 + 450,- 400 + + 450,-1250 + 500,- 400 + 450,- 400 + 450,- 400 + + 450,-1300 + 450,-1250 + 450,-1300 + 400,-1300 + + 450,-1300 + 450,-1250 + 450,-1250 + 500,-1250 + + 450,- 450 + 450,-1250 + 450,-1250 + 500,- 400 + + 450,-1250 + 450,-1300 + 450,-1250 + 450,- 450 + + 450,-1250 + 450,- 400 + 450,- 400 + 500,-1250 + + 450,-1250 + 450,- 400 + 500,- 400 + 450,-1250 + + 450 + Sum: 64300 + */ +// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Panasonic +// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Kaseikyo +// LSB first +// The first two (8-bit) bytes contains the vendor code. +// The next 4 bit is VendorID parity. +// The last byte is parity (XOR) of the 3 bytes before. +// There are multiple interpretations of the next fields: +// IRP: {37k,432}<1,-1|1,-3>(8,-4,M:8,N:8,X:4,D:4,S:8,F:8,G:8,1,-173)+ {X=M:4:0^M:4:4^N:4:0^N:4:4} +// 1. interpretation: 4 bit Device, 8 bitSubdevice and 8 bit function. +// 0_______ 1_______ 2______ 3_______ 4_______ 5_______ +// 01234567 89ABCDEF 01234567 01234567 01234567 01234567 +// 01000000 00100100 0110Dev_ Sub_Dev_ Fun____ XOR( B2, B3, B4) - Byte 0,1 and vendor parity showing Panasonic vendor code 0x2002. +// 1. interpretation: +// see: http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 +// 2. interpretation (Flipper Zero style): +// see: https://www.mikrocontroller.net/articles/IRMP_-_english#KASEIKYO +// Implemented is Samsung style: +// which is derived from Samsung remotes and may not be optimal for Denon kind of Kaseikyo protokol usage. +// +#define KASEIKYO_VENDOR_ID_BITS 16 +#define KASEIKYO_VENDOR_ID_PARITY_BITS 4 +#define KASEIKYO_ADDRESS_BITS 12 +#define KASEIKYO_COMMAND_BITS 8 +#define KASEIKYO_PARITY_BITS 8 +#define KASEIKYO_BITS (KASEIKYO_VENDOR_ID_BITS + KASEIKYO_VENDOR_ID_PARITY_BITS + KASEIKYO_ADDRESS_BITS + KASEIKYO_COMMAND_BITS + KASEIKYO_PARITY_BITS) // 48 +#define KASEIKYO_UNIT 432 // 16 pulses of 37 kHz (432,432432) - Pronto 0x70 | 0x10 + +#define KASEIKYO_HEADER_MARK (8 * KASEIKYO_UNIT) // 3456 +#define KASEIKYO_HEADER_SPACE (4 * KASEIKYO_UNIT) // 1728 + +#define KASEIKYO_BIT_MARK KASEIKYO_UNIT +#define KASEIKYO_ONE_SPACE (3 * KASEIKYO_UNIT) // 1296 +#define KASEIKYO_ZERO_SPACE KASEIKYO_UNIT + +#define KASEIKYO_AVERAGE_DURATION 56000 +#define KASEIKYO_REPEAT_PERIOD 130000 +#define KASEIKYO_REPEAT_DISTANCE (KASEIKYO_REPEAT_PERIOD - KASEIKYO_AVERAGE_DURATION) // 74 ms +#define KASEIKYO_MAXIMUM_REPEAT_DISTANCE (KASEIKYO_REPEAT_DISTANCE + (KASEIKYO_REPEAT_DISTANCE / 4)) // Just a guess + +#define PANASONIC_VENDOR_ID_CODE 0x2002 +#define DENON_VENDOR_ID_CODE 0x3254 +#define MITSUBISHI_VENDOR_ID_CODE 0xCB23 +#define SHARP_VENDOR_ID_CODE 0x5AAA +#define JVC_VENDOR_ID_CODE 0x0103 + +struct PulseDistanceWidthProtocolConstants const KaseikyoProtocolConstants PROGMEM = {KASEIKYO, KASEIKYO_KHZ, KASEIKYO_HEADER_MARK, + KASEIKYO_HEADER_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST + , (KASEIKYO_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr}; + +/************************************ + * Start of send and decode functions + ************************************/ + +/** + * Address can be interpreted as sub-device << 4 + 4 bit device + */ +void IRsend::sendKaseikyo(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint16_t aVendorCode) { + // Set IR carrier frequency + enableIROut (KASEIKYO_KHZ); // 37 kHz + + // Vendor Parity + uint8_t tVendorParity = aVendorCode ^ (aVendorCode >> 8); + tVendorParity = (tVendorParity ^ (tVendorParity >> 4)) & 0xF; + +#if __INT_WIDTH__ < 32 + LongUnion tSendValue; + // Compute parity + tSendValue.UWord.LowWord = (aAddress << KASEIKYO_VENDOR_ID_PARITY_BITS) | tVendorParity; // set low nibble with vendor parity + tSendValue.UBytes[2] = aCommand; + tSendValue.UBytes[3] = aCommand ^ tSendValue.UBytes[0] ^ tSendValue.UBytes[1]; // 8 bit parity of 3 bytes command, address and vendor parity + IRRawDataType tRawKaseikyoData[2]; + tRawKaseikyoData[0] = (uint32_t) tSendValue.UWord.LowWord << 16 | aVendorCode; // LSB of tRawKaseikyoData[0] is sent first + tRawKaseikyoData[1] = tSendValue.UWord.HighWord; + sendPulseDistanceWidthFromArray_P(&KaseikyoProtocolConstants, &tRawKaseikyoData[0], KASEIKYO_BITS, aNumberOfRepeats); +#else + LongLongUnion tSendValue; + tSendValue.UWords[0] = aVendorCode; + // Compute parity + tSendValue.UWords[1] = (aAddress << KASEIKYO_VENDOR_ID_PARITY_BITS) | tVendorParity; // set low nibble to parity + tSendValue.UBytes[4] = aCommand; + tSendValue.UBytes[5] = aCommand ^ tSendValue.UBytes[2] ^ tSendValue.UBytes[3]; // Parity + sendPulseDistanceWidth_P(&KaseikyoProtocolConstants, tSendValue.ULongLong, KASEIKYO_BITS, aNumberOfRepeats); +#endif +} + +/** + * Stub using Kaseikyo with PANASONIC_VENDOR_ID_CODE + */ +void IRsend::sendPanasonic(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, PANASONIC_VENDOR_ID_CODE); +} + +/** + * Stub using Kaseikyo with DENON_VENDOR_ID_CODE + */ +void IRsend::sendKaseikyo_Denon(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, DENON_VENDOR_ID_CODE); +} + +/** + * Stub using Kaseikyo with MITSUBISHI_VENDOR_ID_CODE + */ +void IRsend::sendKaseikyo_Mitsubishi(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, MITSUBISHI_VENDOR_ID_CODE); +} + +/** + * Stub using Kaseikyo with SHARP_VENDOR_ID_CODE + */ +void IRsend::sendKaseikyo_Sharp(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, SHARP_VENDOR_ID_CODE); +} + +/** + * Stub using Kaseikyo with JVC_VENDOR_ID_CODE + */ +void IRsend::sendKaseikyo_JVC(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, JVC_VENDOR_ID_CODE); +} + +/* + * Tested with my Panasonic DVD/TV remote + */ +bool IRrecv::decodeKaseikyo() { + + decode_type_t tProtocol; + // Check we have enough data (96 + 4) 4 for initial gap, start bit mark and space + stop bit mark + if (decodedIRData.rawlen != ((2 * KASEIKYO_BITS) + 4)) { + IR_DEBUG_PRINT(F("Kaseikyo: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not 100")); + return false; + } + + if (!checkHeader_P(&KaseikyoProtocolConstants)) { + return false; + } + + // decode first 16 Vendor ID bits + if (!decodePulseDistanceWidthData_P(&KaseikyoProtocolConstants, KASEIKYO_VENDOR_ID_BITS)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Kaseikyo: ")); + Serial.println(F("Vendor ID decode failed")); +#endif + return false; + } + + uint16_t tVendorId = decodedIRData.decodedRawData; + if (tVendorId == PANASONIC_VENDOR_ID_CODE) { + tProtocol = PANASONIC; + } else if (tVendorId == SHARP_VENDOR_ID_CODE) { + tProtocol = KASEIKYO_SHARP; + } else if (tVendorId == DENON_VENDOR_ID_CODE) { + tProtocol = KASEIKYO_DENON; + } else if (tVendorId == JVC_VENDOR_ID_CODE) { + tProtocol = KASEIKYO_JVC; + } else if (tVendorId == MITSUBISHI_VENDOR_ID_CODE) { + tProtocol = KASEIKYO_MITSUBISHI; + } else { + tProtocol = KASEIKYO; + } + + // Vendor Parity + uint8_t tVendorParity = tVendorId ^ (tVendorId >> 8); + tVendorParity = (tVendorParity ^ (tVendorParity >> 4)) & 0xF; + + /* + * Decode next 32 bits, 8 VendorID parity parity + 12 address (device and subdevice) + 8 command + 8 parity + */ + if (!decodePulseDistanceWidthData_P(&KaseikyoProtocolConstants, + KASEIKYO_VENDOR_ID_PARITY_BITS + KASEIKYO_ADDRESS_BITS + KASEIKYO_COMMAND_BITS + KASEIKYO_PARITY_BITS, + 3 + (2 * KASEIKYO_VENDOR_ID_BITS))) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Kaseikyo: ")); + Serial.println(F("VendorID parity, address, command + parity decode failed")); +#endif + return false; + } + + // Success +// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value + LongUnion tValue; + tValue.ULong = decodedIRData.decodedRawData; +#if __INT_WIDTH__ >= 32 + // workaround until complete refactoring for 64 bit + decodedIRData.decodedRawData = (decodedIRData.decodedRawData << 16) | tVendorId; // store all 48 bits in decodedRawData +#endif + decodedIRData.address = (tValue.UWord.LowWord >> KASEIKYO_VENDOR_ID_PARITY_BITS); // remove 4 bit vendor parity + decodedIRData.command = tValue.UByte.MidHighByte; + uint8_t tParity = tValue.UByte.LowByte ^ tValue.UByte.MidLowByte ^ tValue.UByte.MidHighByte; + + if (tVendorParity != (tValue.UByte.LowByte & 0xF)) { + decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_LSB_FIRST; + +#if defined(LOCAL_DEBUG) + Serial.print(F("Kaseikyo: ")); + Serial.print(F("4 bit VendorID parity is not correct. Expected=0x")); + Serial.print(tVendorParity, HEX); + Serial.print(F(" received=0x")); + Serial.print(decodedIRData.decodedRawData, HEX); + Serial.print(F(" VendorID=0x")); + Serial.println(tVendorId, HEX); +#endif + } + + if (tProtocol == KASEIKYO) { + decodedIRData.flags |= IRDATA_FLAGS_EXTRA_INFO; + decodedIRData.extra = tVendorId; // Store (unknown) vendor ID + } + + if (tValue.UByte.HighByte != tParity) { + decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED; + +#if defined(LOCAL_DEBUG) + Serial.print(F("Kaseikyo: ")); + Serial.print(F("8 bit parity is not correct. Expected=0x")); + Serial.print(tParity, HEX); + Serial.print(F(" received=0x")); + Serial.print(decodedIRData.decodedRawData >> KASEIKYO_COMMAND_BITS, HEX); + Serial.print(F(" address=0x")); + Serial.print(decodedIRData.address, HEX); + Serial.print(F(" command=0x")); + Serial.println(decodedIRData.command, HEX); +#endif + } + + decodedIRData.numberOfBits = KASEIKYO_BITS; + decodedIRData.protocol = tProtocol; + + // check for repeat + checkForRepeatSpaceTicksAndSetFlag(KASEIKYO_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK); + + return true; +} + +/* + * Removed void IRsend::sendPanasonic(uint16_t aAddress, uint32_t aData) + * and bool IRrecv::decodePanasonicMSB(decode_results *aResults) + * since their implementations were wrong (wrong length), and nobody recognized it + */ + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_KASEIKYO_HPP diff --git a/src/ir_LG.cpp b/src/ir_LG.cpp deleted file mode 100644 index c5bb497de..000000000 --- a/src/ir_LG.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "IRremote.h" - -//============================================================================== -// L GGGG -// L G -// L G GG -// L G G -// LLLLL GGG -//============================================================================== - -#define LG_BITS 28 - -#define LG_HEADER_MARK 8400 -#define LG_HEADER_SPACE 4200 -#define LG_BIT_MARK 600 -#define LG_ONE_SPACE 1600 -#define LG_ZERO_SPACE 550 - -//+============================================================================= -#if DECODE_LG -bool IRrecv::decodeLG() { - int offset = 1; // Skip first space - - // Check we have the right amount of data +3 for start bit mark and space + stop bit mark - if (irparams.rawlen <= (2 * LG_BITS) + 3) - return false; - - // Initial mark/space - if (!MATCH_MARK(results.rawbuf[offset], LG_HEADER_MARK)) { - return false; - } - offset++; - - if (!MATCH_SPACE(results.rawbuf[offset], LG_HEADER_SPACE)) { - return false; - } - offset++; - - if (!decodePulseDistanceData(LG_BITS, offset, LG_BIT_MARK, LG_ONE_SPACE, LG_ZERO_SPACE)) { - return false; - } - // Stop bit - if (!MATCH_MARK(results.rawbuf[offset + (2 * LG_BITS)], LG_BIT_MARK)) { - DBG_PRINT("Stop bit verify failed"); - return false; - } - - // Success - results.bits = LG_BITS; - results.decode_type = LG; - return true; -} - -bool IRrecv::decodeLG(decode_results *aResults) { - bool aReturnValue = decodeLG(); - *aResults = results; - return aReturnValue; -} -#endif - -//+============================================================================= -#if SEND_LG -void IRsend::sendLG(unsigned long data, int nbits) { - // Set IR carrier frequency - enableIROut(38); - - // Header - mark(LG_HEADER_MARK); - space(LG_HEADER_SPACE); -// mark(LG_BIT_MARK); - - // Data - sendPulseDistanceWidthData(LG_BIT_MARK, LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, data, nbits); - - mark(LG_BIT_MARK); - space(0); // Always end with the LED off -} -#endif - diff --git a/src/ir_LG.hpp b/src/ir_LG.hpp new file mode 100644 index 000000000..ad35f8126 --- /dev/null +++ b/src/ir_LG.hpp @@ -0,0 +1,349 @@ +/* + * ir_LG.hpp + * + * Contains functions for receiving and sending LG IR Protocol for air conditioner + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2017-2024 Darryl Smith, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_LG_HPP +#define _IR_LG_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +//============================================================================== +// L GGGG +// L G +// L G GG +// L G G +// LLLLL GGG +//============================================================================== +/* + * Protocol=LG Address=0xF1 Command=0x7776 Raw-Data=0xF17776B 28 bits MSB first + +8950,-4150 + + 500,-1550 + 550,-1550 + 500,-1550 + 500,-1600 + + 500,- 700 + 350,- 600 + 450,- 600 + 450,-1550 + + 500,- 550 + 500,-1550 + 500,-1600 + 500,-1550 + + 550,- 550 + 500,-1550 + 500,-1550 + 550,-1550 + + 500,- 550 + 500,-1550 + 500,-1600 + 500,-1550 + + 500,- 550 + 500,-1550 + 500,-1600 + 500,- 550 + + 500,-1550 + 500,- 600 + 450,-1600 + 500,-1550 + + 500 + Sum: 62400 + */ + +// LG originally added by Darryl Smith (based on the JVC protocol) +// see: https://github.com/Arduino-IRremote/Arduino-IRremote/tree/master/examples/LGAirConditionerSendDemo +// see: https://www.mikrocontroller.net/articles/IRMP_-_english#LGAIR +// MSB first, 1 start bit + 8 bit address + 16 bit command + 4 bit checksum + 1 stop bit (28 data bits). +// Bit and repeat timing is like NEC +// LG2 has different header timing and a shorter bit time +/* + * LG remote IR-LED measurements: Type AKB 73315611 for air conditioner, Ver1.1 from 2011.03.01 + * Protocol: LG2 + * Internal crystal: 4 MHz + * Header: 8.9 ms mark 4.15 ms space + * Data: 500 / 540 and 500 / 1580; + * Clock is not synchronized with gate so you have 19 and sometimes 19 and a spike pulses for mark + * Duty: 9 us on 17 us off => around 33 % duty + * NO REPEAT: If value like temperature has changed during long press, the last value is send at button release. + * If you do a double press, the next value can be sent after around 118 ms. Tested with the fan button. + + * LG remote IR-LED measurements: Type AKB 75095308 for LG TV + * Protocol: NEC!!! + * Frequency 37.88 kHz + * Header: 9.0 ms mark 4.5 ms space + * Data: 560 / 560 and 560 / 1680; + * Clock is synchronized with gate, mark always starts with a full period + * Duty: 13 us on 13 us off => 50 % duty + * Repeat: 110 ms 9.0 ms mark, 2250 us space, 560 stop + * LSB first! + * + * The codes of the LG air conditioner are documented in https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/ac_LG.cpp + */ +#define LG_ADDRESS_BITS 8 +#define LG_COMMAND_BITS 16 +#define LG_CHECKSUM_BITS 4 +#define LG_BITS (LG_ADDRESS_BITS + LG_COMMAND_BITS + LG_CHECKSUM_BITS) // 28 + +#define LG_UNIT 500 // 19 periods of 38 kHz + +#define LG_HEADER_MARK (18 * LG_UNIT) // 9000 +#define LG_HEADER_SPACE 4200 // 4200 | 84 + +#define LG2_HEADER_MARK (19 * LG_UNIT) // 9500 +#define LG2_HEADER_SPACE (6 * LG_UNIT) // 3000 + +#define LG_BIT_MARK LG_UNIT +#define LG_ONE_SPACE 1580 // 60 periods of 38 kHz +#define LG_ZERO_SPACE 550 + +#define LG_REPEAT_HEADER_SPACE (4 * LG_UNIT) // 2250 +#define LG_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down. +//#define LG_AVERAGE_DURATION 58000 // LG_HEADER_MARK + LG_HEADER_SPACE + 32 * 2,5 * LG_UNIT) + LG_UNIT // 2.5 because we assume more zeros than ones +//#define LG_REPEAT_DURATION (LG_HEADER_MARK + LG_REPEAT_HEADER_SPACE + LG_BIT_MARK) +//#define LG_REPEAT_DISTANCE (LG_REPEAT_PERIOD - LG_AVERAGE_DURATION) // 52 ms + +struct PulseDistanceWidthProtocolConstants const LGProtocolConstants PROGMEM= {LG, LG_KHZ, LG_HEADER_MARK, LG_HEADER_SPACE, LG_BIT_MARK, + LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, (LG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), &sendNECSpecialRepeat}; + +struct PulseDistanceWidthProtocolConstants const LG2ProtocolConstants PROGMEM = {LG2, LG_KHZ, LG2_HEADER_MARK, LG2_HEADER_SPACE, LG_BIT_MARK, + LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, (LG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), &sendLG2SpecialRepeat}; + +/************************************ + * Start of send and decode functions + ************************************/ +/* + * Send special LG2 repeat - not used internally + */ +void IRsend::sendLG2Repeat() { + enableIROut (LG_KHZ); // 38 kHz + mark(LG2_HEADER_MARK); // + 3000 + space(LG_REPEAT_HEADER_SPACE); // - 2250 + mark(LG_BIT_MARK); // + 500 +} + +/** + * Static function for sending special repeat frame. + * For use in ProtocolConstants. Saves up to 250 bytes compared to a member function. + */ +void sendLG2SpecialRepeat() { + IrSender.enableIROut(LG_KHZ); // 38 kHz + IrSender.mark(LG2_HEADER_MARK); // + 3000 + IrSender.space(LG_REPEAT_HEADER_SPACE); // - 2250 + IrSender.mark(LG_BIT_MARK); // + 500 +} + +uint32_t IRsend::computeLGRawDataAndChecksum(uint8_t aAddress, uint16_t aCommand) { + uint32_t tRawData = ((uint32_t) aAddress << (LG_COMMAND_BITS + LG_CHECKSUM_BITS)) | ((uint32_t) aCommand << LG_CHECKSUM_BITS); + /* + * My guess of the 4 bit checksum + * Addition of all 4 nibbles of the 16 bit command + */ + uint8_t tChecksum = 0; + uint16_t tTempForChecksum = aCommand; + for (int i = 0; i < 4; ++i) { + tChecksum += tTempForChecksum & 0xF; // add low nibble + tTempForChecksum >>= 4; // shift by a nibble + } + return (tRawData | (tChecksum & 0xF)); +} + +/** + * LG uses the NEC repeat. + */ +void IRsend::sendLG(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) { + sendPulseDistanceWidth_P(&LGProtocolConstants, computeLGRawDataAndChecksum(aAddress, aCommand), LG_BITS, aNumberOfRepeats); +} + +/** + * LG2 uses a special repeat. + */ +void IRsend::sendLG2(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) { + sendPulseDistanceWidth_P(&LG2ProtocolConstants, computeLGRawDataAndChecksum(aAddress, aCommand), LG_BITS, aNumberOfRepeats); +} + +bool IRrecv::decodeLG() { + decode_type_t tProtocol = LG; + uint16_t tHeaderSpace = LG_HEADER_SPACE; + + /* + * First check for right data length + * Next check start bit + * Next try the decode + */ + +// Check we have the right amount of data (60). The +4 is for initial gap, start bit mark and space + stop bit mark. + if (decodedIRData.rawlen != ((2 * LG_BITS) + 4) && (decodedIRData.rawlen != 4)) { + IR_DEBUG_PRINT(F("LG: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not 60 or 4")); + return false; + } + +// Check header "mark" this must be done for repeat and data + if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], LG_HEADER_MARK)) { + if (matchMark(decodedIRData.rawDataPtr->rawbuf[1], LG2_HEADER_MARK)) { + tProtocol = LG2; + tHeaderSpace = LG2_HEADER_SPACE; + } else { +#if defined(LOCAL_DEBUG) + Serial.print(F("LG: ")); + Serial.println(F("Header mark is wrong")); +#endif + return false; // neither LG nor LG2 header + } + } + +// Check for repeat - here we have another header space length + if (decodedIRData.rawlen == 4) { + if (matchSpace(decodedIRData.rawDataPtr->rawbuf[2], LG_REPEAT_HEADER_SPACE) + && matchMark(decodedIRData.rawDataPtr->rawbuf[3], LG_BIT_MARK)) { + decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_MSB_FIRST; + decodedIRData.address = lastDecodedAddress; + decodedIRData.command = lastDecodedCommand; + decodedIRData.protocol = lastDecodedProtocol; + return true; + } +#if defined(LOCAL_DEBUG) + Serial.print(F("LG: ")); + Serial.print(F("Repeat header space is wrong")); +#endif + return false; + } + +// Check command header space + if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], tHeaderSpace)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("LG: ")); + Serial.println(F("Header space length is wrong")); +#endif + return false; + } + + if (!decodePulseDistanceWidthData_P(&LGProtocolConstants, LG_BITS)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("LG: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } + +// Success + decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; + decodedIRData.command = (decodedIRData.decodedRawData >> LG_CHECKSUM_BITS) & 0xFFFF; + decodedIRData.address = decodedIRData.decodedRawData >> (LG_COMMAND_BITS + LG_CHECKSUM_BITS); // first 8 bit + + /* + * My guess of the checksum + */ + uint8_t tChecksum = 0; + uint16_t tTempForChecksum = decodedIRData.command; + for (int i = 0; i < 4; ++i) { + tChecksum += tTempForChecksum & 0xF; // add low nibble + tTempForChecksum >>= 4; // shift by a nibble + } +// Checksum check + if ((tChecksum & 0xF) != (decodedIRData.decodedRawData & 0xF)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("LG: ")); + Serial.print(F("4 bit checksum is not correct. expected=0x")); + Serial.print(tChecksum, HEX); + Serial.print(F(" received=0x")); + Serial.print((decodedIRData.decodedRawData & 0xF), HEX); + Serial.print(F(" data=0x")); + Serial.println(decodedIRData.command, HEX); +#endif + decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED; + } + + decodedIRData.protocol = tProtocol; // LG or LG2 + decodedIRData.numberOfBits = LG_BITS; + + return true; +} + +/********************************************************************************* + * Old deprecated functions, kept for backward compatibility to old 2.0 tutorials + *********************************************************************************/ + +/** + * Here you can put your raw data, even one with "wrong" checksum. + * @param aRawData The lowest 28 (LG_BITS) bit of this value are sent MSB first. + * @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent. + */ +void IRsend::sendLGRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats) { + sendPulseDistanceWidth_P(&LGProtocolConstants, aRawData, LG_BITS, aNumberOfRepeats); +} + +bool IRrecv::decodeLGMSB(decode_results *aResults) { + unsigned int offset = 1; // Skip first space + +// Check we have enough data (60) - +4 for initial gap, start bit mark and space + stop bit mark + if (aResults->rawlen != (2 * LG_BITS) + 4) { + return false; + } + +// Initial mark/space + if (!matchMark(aResults->rawbuf[offset], LG_HEADER_MARK)) { + return false; + } + offset++; + + if (!matchSpace(aResults->rawbuf[offset], LG_HEADER_SPACE)) { + return false; + } + offset++; + + if (!decodePulseDistanceWidthData(LG_BITS, offset, LG_BIT_MARK, LG_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST)) { + return false; + } +// Stop bit + if (!matchMark(aResults->rawbuf[offset + (2 * LG_BITS)], LG_BIT_MARK)) { +#if defined(LOCAL_DEBUG) + Serial.println(F("Stop bit mark length is wrong")); +#endif + return false; + } + +// Success + aResults->value = decodedIRData.decodedRawData; + aResults->bits = LG_BITS; + aResults->decode_type = LG; + decodedIRData.protocol = LG; + return true; +} + +//+============================================================================= +void IRsend::sendLG(unsigned long data, int nbits) { +// Set IR carrier frequency + enableIROut (LG_KHZ); +#if !(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)) +// Serial.println(F( +// "The function sendLG(data, nbits) is deprecated and may not work as expected! Use sendLGRaw(data, NumberOfRepeats) or better sendLG(Address, Command, NumberOfRepeats).")); +#endif +// Header + mark(LG_HEADER_MARK); + space(LG_HEADER_SPACE); +// mark(LG_BIT_MARK); + +// Data + stop bit + sendPulseDistanceWidthData(LG_BIT_MARK, LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, data, nbits, PROTOCOL_IS_MSB_FIRST); +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_LG_HPP diff --git a/src/ir_Lego.hpp b/src/ir_Lego.hpp new file mode 100644 index 000000000..156bf7439 --- /dev/null +++ b/src/ir_Lego.hpp @@ -0,0 +1,210 @@ +/* + * ir_Lego.hpp + * + * Contains functions for receiving and sending Lego Power Functions IR Protocol + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2023 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_LEGO_HPP +#define _IR_LEGO_HPP + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +//============================================================================== +// L EEEEEE EEEE OOOO +// L E E O O +// L EEEE E EEE O O +// L E E E O O +// LLLLLL EEEEEE EEEE OOOO +//============================================================================== +// from LEGO Power Functions RC Manual 26.02.2010 Version 1.20 +// https://github.com/jurriaan/Arduino-PowerFunctions/raw/master/LEGO_Power_Functions_RC_v120.pdf +// https://oberguru.net/elektronik/ir/codes/lego_power_functions_train.lircd.conf +// For original LEGO receiver see: https://www.philohome.com/pfrec/pfrec.htm and https://www.youtube.com/watch?v=KCM4Ug1bPrM +// +// To ensure correct detection of IR messages six 38 kHz cycles are transmitted as mark. +// Low bit consists of 6 cycles of IR and 10 ļæ½cyclesļæ½ of pause, +// high bit of 6 cycles IR and 21 ļæ½cyclesļæ½ of pause and start bit of 6 cycles IR and 39 ļæ½cyclesļæ½ of pause. +// Low bit range 316 - 526 us +// High bit range 526 ļæ½ 947 us +// Start/stop bit range 947 ļæ½ 1579 us +// If tm is the maximum message length (16ms) and Ch is the channel number, then +// The delay before transmitting the first message is: (4 ļæ½ Ch)*tm +// The time from start to start for the next 2 messages is: 5*tm +// The time from start to start for the following messages is: (6 + 2*Ch)*tm +// Supported Devices +// LEGO Power Functions IR Receiver 8884 +// MSB first, 1 start bit + 4 bit channel, 4 bit mode + 4 bit command + 4 bit parity + 1 stop bit. +#define LEGO_CHANNEL_BITS 4 +#define LEGO_MODE_BITS 4 +#define LEGO_COMMAND_BITS 4 +#define LEGO_PARITY_BITS 4 + +#define LEGO_BITS (LEGO_CHANNEL_BITS + LEGO_MODE_BITS + LEGO_COMMAND_BITS + LEGO_PARITY_BITS) + +#define LEGO_HEADER_MARK 158 // 6 cycles +#define LEGO_HEADER_SPACE 1026 // 39 cycles + +#define LEGO_BIT_MARK 158 // 6 cycles +#define LEGO_ONE_SPACE 553 // 21 cycles +#define LEGO_ZERO_SPACE 263 // 10 cycles + +#define LEGO_AVERAGE_DURATION 11000 // LEGO_HEADER_MARK + LEGO_HEADER_SPACE + 16 * 600 + 158 + +#define LEGO_AUTO_REPEAT_PERIOD_MIN 110000 // Every frame is auto repeated 5 times. +#define LEGO_AUTO_REPEAT_PERIOD_MAX 230000 // space for channel 3 + +#define LEGO_MODE_EXTENDED 0 +#define LEGO_MODE_COMBO 1 +#define LEGO_MODE_SINGLE 0x4 // here the 2 LSB have meanings like Output A / Output B + +struct PulseDistanceWidthProtocolConstants LegoProtocolConstants = { LEGO_PF, 38, LEGO_HEADER_MARK, LEGO_HEADER_SPACE, +LEGO_BIT_MARK, LEGO_ONE_SPACE, LEGO_BIT_MARK, LEGO_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (LEGO_AUTO_REPEAT_PERIOD_MIN + / MICROS_IN_ONE_MILLI), nullptr }; + +/************************************ + * Start of send and decode functions + ************************************/ +/* + * Here we process the structured data, and call the send raw data function + * @param aMode one of LEGO_MODE_EXTENDED, LEGO_MODE_COMBO, LEGO_MODE_SINGLE + */ +void IRsend::sendLegoPowerFunctions(uint8_t aChannel, uint8_t aCommand, uint8_t aMode, bool aDoSend5Times) { + aChannel &= 0x0F; // allow toggle and escape bits too + aCommand &= 0x0F; + aMode &= 0x0F; + uint8_t tParity = 0xF ^ aChannel ^ aMode ^ aCommand; + // send 4 bit channel, 4 bit mode, 4 bit command, 4 bit parity + uint16_t tRawData = (((aChannel << LEGO_MODE_BITS) | aMode) << (LEGO_COMMAND_BITS + LEGO_PARITY_BITS)) + | (aCommand << LEGO_PARITY_BITS) | tParity; + sendLegoPowerFunctions(tRawData, aChannel, aDoSend5Times); +} + +void IRsend::sendLegoPowerFunctions(uint16_t aRawData, uint8_t aChannel, bool aDoSend5Times) { + + IR_DEBUG_PRINT(F("sendLego aRawData=0x")); + IR_DEBUG_PRINTLN(aRawData, HEX); + + aChannel &= 0x03; // we have 4 channels + + uint_fast8_t tNumberOfRepeats = 0; + if (aDoSend5Times) { + tNumberOfRepeats = 4; + } +// required for repeat timing, see http://www.hackvandedam.nl/blog/?page_id=559 + uint8_t tRepeatPeriod = (LEGO_AUTO_REPEAT_PERIOD_MIN / MICROS_IN_ONE_MILLI) + (aChannel * 40); // from 110 to 230 + LegoProtocolConstants.RepeatPeriodMillis = tRepeatPeriod; + sendPulseDistanceWidth(&LegoProtocolConstants, aRawData, LEGO_BITS, tNumberOfRepeats); +} + +/* + * Mode is stored in the upper nibble of command + */ +bool IRrecv::decodeLegoPowerFunctions() { + + if (!checkHeader(&LegoProtocolConstants)) { + return false; + } + + // Check we have enough data - +4 for initial gap, start bit mark and space + stop bit mark + if (decodedIRData.rawlen != (2 * LEGO_BITS) + 4) { + IR_DEBUG_PRINT(F("LEGO: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen); + IR_DEBUG_PRINTLN(F(" is not 36")); + return false; + } + + if (!decodePulseDistanceWidthData(&LegoProtocolConstants, LEGO_BITS)) { + IR_DEBUG_PRINT(F("LEGO: ")); + IR_DEBUG_PRINTLN(F("Decode failed")); + return false; + } + + // Stop bit + if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * LEGO_BITS)], LEGO_BIT_MARK)) { + IR_DEBUG_PRINT(F("LEGO: ")); + IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong")); + return false; + } + + // Success + decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; + uint16_t tDecodedValue = decodedIRData.decodedRawData; + uint8_t tToggleEscapeChannel = tDecodedValue >> (LEGO_MODE_BITS + LEGO_COMMAND_BITS + LEGO_PARITY_BITS); + uint8_t tMode = (tDecodedValue >> (LEGO_COMMAND_BITS + LEGO_PARITY_BITS)) & 0xF; + uint8_t tData = (tDecodedValue >> LEGO_PARITY_BITS) & 0xF; // lego calls this field "data" + uint8_t tParityReceived = tDecodedValue & 0xF; + + // This is parity as defined in the specifications + // But in some scans I saw 0x9 ^ .. as parity formula + uint8_t tParityComputed = 0xF ^ tToggleEscapeChannel ^ tMode ^ tData; + + // parity check + if (tParityReceived != tParityComputed) { + IR_DEBUG_PRINT(F("LEGO: ")); + IR_DEBUG_PRINT(F("Parity is not correct. expected=0x")); + IR_DEBUG_PRINT(tParityComputed, HEX); + IR_DEBUG_PRINT(F(" received=0x")); + IR_DEBUG_PRINT(tParityReceived, HEX); + IR_DEBUG_PRINT(F(", raw=0x")); + IR_DEBUG_PRINT(tDecodedValue, HEX); + IR_DEBUG_PRINT(F(", 3 nibbles are 0x")); + IR_DEBUG_PRINT(tToggleEscapeChannel, HEX); + IR_DEBUG_PRINT(F(", 0x")); + IR_DEBUG_PRINT(tMode, HEX); + IR_DEBUG_PRINT(F(", 0x")); + IR_DEBUG_PRINTLN(tData, HEX); + // might not be an error, so just continue + decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_MSB_FIRST; + } + + /* + * Check for autorepeat (should happen 4 times for one press) + */ + if (decodedIRData.initialGapTicks < (LEGO_AUTO_REPEAT_PERIOD_MAX / MICROS_PER_TICK)) { + decodedIRData.flags |= IRDATA_FLAGS_IS_AUTO_REPEAT; + } + decodedIRData.address = tToggleEscapeChannel; + decodedIRData.command = tData | tMode << LEGO_COMMAND_BITS; + decodedIRData.numberOfBits = LEGO_BITS; + decodedIRData.protocol = LEGO_PF; + + return true; +} + +/********************************************************************************* + * Old deprecated functions, kept for backward compatibility to old 2.0 tutorials + *********************************************************************************/ + +void IRsend::sendLegoPowerFunctions(uint16_t aRawData, bool aDoSend5Times) { + sendLegoPowerFunctions(aRawData, (aRawData >> (LEGO_MODE_BITS + LEGO_COMMAND_BITS + LEGO_PARITY_BITS)) & 0x3, aDoSend5Times); +} + +/** @}*/ +#endif // _IR_LEGO_HPP diff --git a/src/ir_Lego_PF.cpp b/src/ir_Lego_PF.cpp deleted file mode 100644 index 8921c0acf..000000000 --- a/src/ir_Lego_PF.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "IRremote.h" -#include "ir_Lego_PF_BitStreamEncoder.h" - -//============================================================================== -// L EEEEEE EEEE OOOO -// L E E O O -// L EEEE E EEE O O -// L E E E O O LEGO Power Functions -// LLLLLL EEEEEE EEEE OOOO Copyright (c) 2016 Philipp Henkel -//============================================================================== - -// Supported Devices -// LEGOĀ® Power Functions IR Receiver 8884 -/* - * Lego Power Functions receive. - * As per document - * http://cache.lego.com/Media/Download/PowerfunctionsElementSpecsDownloads/otherfiles/download9FC026117C091015E81EC28101DACD4E/8884RemoteControlIRReceiver_Download.pdf - - * Receives the 16 bit protocol. It can be decoded with the Open Powerfunctions code - * https://bitbucket.org/tinkerer_/openpowerfunctionsrx - */ - -//+============================================================================= -// -#if SEND_LEGO_PF -#if DEBUG -namespace { -void logFunctionParameters(uint16_t data, bool repeat) { - DBG_PRINT("sendLegoPowerFunctions(data="); - DBG_PRINT(data); - DBG_PRINT(", repeat="); - DBG_PRINTLN(repeat?"true)" : "false)"); -} -} // anonymous namespace -#endif // DEBUG - -void IRsend::sendLegoPowerFunctions(uint16_t data, bool repeat) { -#if DEBUG - ::logFunctionParameters(data, repeat); -#endif // DEBUG - - enableIROut(38); - static LegoPfBitStreamEncoder bitStreamEncoder; - bitStreamEncoder.reset(data, repeat); - do { - mark(bitStreamEncoder.getMarkDuration()); - space_long(bitStreamEncoder.getPauseDuration()); - } while (bitStreamEncoder.next()); -} - -#endif // SEND_LEGO_PF - -#if DECODE_LEGO_PF -/* - * UNTESTED!!! - */ -#define LEGO_PF_STARTSTOP 1579 -#define LEGO_PF_LOWBIT 526 -#define LEGO_PF_HIBIT 947 -#define LEGO_PF_LOWER 315 -#define LEGO_PF_BITS 16 // The number of bits in the command - -bool IRrecv::decodeLegoPowerFunctions() { - unsigned long data = 0; // Somewhere to build our code - DBG_PRINTLN(results.rawlen, DEC); - // Check we have the right amount of data - if (irparams.rawlen != (2 * LEGO_PF_BITS) + 4) - return false; - - DBG_PRINTLN("Attempting Lego Power Functions Decode"); - - uint16_t desired_us = (results.rawbuf[1] + results.rawbuf[2]) * MICROS_PER_TICK; - DBG_PRINT("PF desired_us = "); - DBG_PRINTLN(desired_us, DEC); - - if (desired_us > LEGO_PF_HIBIT && desired_us <= LEGO_PF_STARTSTOP) { - DBG_PRINTLN("Found PF Start Bit"); - int offset = 3; - for (int i = 0; i < LEGO_PF_BITS; i++) { - desired_us = (results.rawbuf[offset] + results.rawbuf[offset + 1]) * MICROS_PER_TICK; - - DBG_PRINT("PF desired_us = "); - DBG_PRINTLN(desired_us, DEC); - if (desired_us >= LEGO_PF_LOWER && desired_us <= LEGO_PF_LOWBIT) { - DBG_PRINTLN("PF 0"); - data = (data << 1) | 0; - } else if (desired_us > LEGO_PF_LOWBIT && desired_us <= LEGO_PF_HIBIT) { - DBG_PRINTLN("PF 1"); - data = (data << 1) | 1; - } else { - DBG_PRINTLN("PF Failed"); - return false; - } - offset += 2; - } - - desired_us = (results.rawbuf[offset]) * MICROS_PER_TICK; - - DBG_PRINT("PF END desired_us = "); - DBG_PRINTLN(desired_us, DEC); - if (desired_us < LEGO_PF_LOWER) { - DBG_PRINTLN("Found PF End Bit"); - DBG_PRINTLN(data, BIN); - - // Success - results.bits = LEGO_PF_BITS; - results.value = data; - results.decode_type = LEGO_PF; - return true; - } - } - return false; -} -#endif diff --git a/src/ir_Lego_PF_BitStreamEncoder.h b/src/ir_Lego_PF_BitStreamEncoder.h deleted file mode 100644 index d49219c02..000000000 --- a/src/ir_Lego_PF_BitStreamEncoder.h +++ /dev/null @@ -1,117 +0,0 @@ -//============================================================================== -// L EEEEEE EEEE OOOO -// L E E O O -// L EEEE E EEE O O -// L E E E O O LEGO Power Functions -// LLLLLL EEEEEE EEEE OOOO Copyright (c) 2016, 2017 Philipp Henkel -//============================================================================== - -//+============================================================================= -// - -class LegoPfBitStreamEncoder { -private: - uint16_t data; - bool repeatMessage; - uint8_t messageBitIdx; - uint8_t repeatCount; - uint16_t messageLength; - -public: - // HIGH data bit = IR mark + high pause - // LOW data bit = IR mark + low pause - static const uint16_t LOW_BIT_DURATION = 421; - static const uint16_t HIGH_BIT_DURATION = 711; - static const uint16_t START_BIT_DURATION = 1184; - static const uint16_t STOP_BIT_DURATION = 1184; - static const uint8_t IR_MARK_DURATION = 158; - static const uint16_t HIGH_PAUSE_DURATION = HIGH_BIT_DURATION - IR_MARK_DURATION; - static const uint16_t LOW_PAUSE_DURATION = LOW_BIT_DURATION - IR_MARK_DURATION; - static const uint16_t START_PAUSE_DURATION = START_BIT_DURATION - IR_MARK_DURATION; - static const uint16_t STOP_PAUSE_DURATION = STOP_BIT_DURATION - IR_MARK_DURATION; - static const uint8_t MESSAGE_BITS = 18; - static const uint16_t MAX_MESSAGE_LENGTH = 16000; - - void reset(uint16_t data, bool repeatMessage) { - this->data = data; - this->repeatMessage = repeatMessage; - messageBitIdx = 0; - repeatCount = 0; - messageLength = getMessageLength(); - } - - int getChannelId() const { - return 1 + ((data >> 12) & 0x3); - } - - uint16_t getMessageLength() const { - // Sum up all marks - uint16_t length = MESSAGE_BITS * IR_MARK_DURATION; - - // Sum up all pauses - length += START_PAUSE_DURATION; - for (unsigned long mask = 1UL << 15; mask; mask >>= 1) { - if (data & mask) { - length += HIGH_PAUSE_DURATION; - } else { - length += LOW_PAUSE_DURATION; - } - } - length += STOP_PAUSE_DURATION; - return length; - } - - boolean next() { - messageBitIdx++; - if (messageBitIdx >= MESSAGE_BITS) { - repeatCount++; - messageBitIdx = 0; - } - if (repeatCount >= 1 && !repeatMessage) { - return false; - } else if (repeatCount >= 5) { - return false; - } else { - return true; - } - } - - uint8_t getMarkDuration() const { - return IR_MARK_DURATION; - } - - uint32_t getPauseDuration() const { - if (messageBitIdx == 0) - return START_PAUSE_DURATION; - else if (messageBitIdx < MESSAGE_BITS - 1) { - return getDataBitPause(); - } else { - return getStopPause(); - } - } - -private: - uint16_t getDataBitPause() const { - const int pos = MESSAGE_BITS - 2 - messageBitIdx; - const bool isHigh = data & (1 << pos); - return isHigh ? HIGH_PAUSE_DURATION : LOW_PAUSE_DURATION; - } - - uint32_t getStopPause() const { - if (repeatMessage) { - return getRepeatStopPause(); - } else { - return STOP_PAUSE_DURATION; - } - } - - uint32_t getRepeatStopPause() const { - if (repeatCount == 0 || repeatCount == 1) { - return STOP_PAUSE_DURATION + (uint32_t) 5 * MAX_MESSAGE_LENGTH - messageLength; - } else if (repeatCount == 2 || repeatCount == 3) { - return STOP_PAUSE_DURATION + (uint32_t) (6 + 2 * getChannelId()) * MAX_MESSAGE_LENGTH - messageLength; - } else { - return STOP_PAUSE_DURATION; - } - } -}; diff --git a/src/ir_MagiQuest.cpp b/src/ir_MagiQuest.cpp deleted file mode 100644 index 621837586..000000000 --- a/src/ir_MagiQuest.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include "IRremote.h" - -// Based off the Magiquest fork of Arduino-IRremote by mpflaga -// https://github.com/mpflaga/Arduino-IRremote/ - -//============================================================================== -// -// -// M A G I Q U E S T -// -// -//============================================================================== - -// MagiQuest packet is both Wand ID and magnitude of swish and flick -union magiquest_t { - unsigned long long llword; - struct { - unsigned int magnitude; - unsigned long wand_id; - char padding; - char scrap; // just to pad the struct out to 64 bits so we can union with llword - } cmd; -}; - -#define MAGIQUEST_BITS 50 // The number of bits in the command itself -#define MAGIQUEST_PERIOD 1150 // Length of time a full MQ "bit" consumes (1100 - 1200 usec) -/* - * 0 = 25% mark & 75% space across 1 period - * 1150 * 0.25 = 288 usec mark - * 1150 - 288 = 862 usec space - * 1 = 50% mark & 50% space across 1 period - * 1150 * 0.5 = 575 usec mark - * 1150 - 575 = 575 usec space - */ -#define MAGIQUEST_ONE_MARK 575 -#define MAGIQUEST_ONE_SPACE 575 -#define MAGIQUEST_ZERO_MARK 288 -#define MAGIQUEST_ZERO_SPACE 862 - -#define MAGIQUEST_MASK (1ULL << (MAGIQUEST_BITS-1)) - -//+============================================================================= -// -#if SEND_MAGIQUEST -void IRsend::sendMagiQuest(unsigned long wand_id, unsigned int magnitude) { - magiquest_t data; - - data.llword = 0; - data.cmd.wand_id = wand_id; - data.cmd.magnitude = magnitude; - - // Set IR carrier frequency - enableIROut(38); - - // Data - for (unsigned long long mask = MAGIQUEST_MASK; mask > 0; mask >>= 1) { - if (data.llword & mask) { - DBG_PRINT("1"); - mark(MAGIQUEST_ONE_MARK); - space(MAGIQUEST_ONE_SPACE); - } else { - DBG_PRINT("0"); - mark(MAGIQUEST_ZERO_MARK); - space(MAGIQUEST_ZERO_SPACE); - } - } - DBG_PRINTLN(""); - - // Footer - mark(MAGIQUEST_ZERO_MARK); - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -// -#if DECODE_MAGIQUEST -bool IRrecv::decodeMagiQuest() { - magiquest_t data; // Somewhere to build our code - unsigned int offset = 1; // Skip the gap reading - - unsigned int mark_; - unsigned int space_; - unsigned int ratio_; - -#if DEBUG - char bitstring[MAGIQUEST_BITS*2]; - memset(bitstring, 0, sizeof(bitstring)); -#endif - - // Check we have enough data - if (results.rawlen < 2 * MAGIQUEST_BITS) { - DBG_PRINT("Not enough bits to be a MagiQuest packet ("); - DBG_PRINT(irparams.rawlen); - DBG_PRINT(" < "); - DBG_PRINT(MAGIQUEST_BITS*2); - DBG_PRINTLN(")"); - return false; - } - - // Read the bits in - data.llword = 0; - while (offset + 1 < results.rawlen) { - mark_ = results.rawbuf[offset++]; - space_ = results.rawbuf[offset++]; - ratio_ = space_ / mark_; - - DBG_PRINT("mark="); - DBG_PRINT(mark_ * MICROS_PER_TICK); - DBG_PRINT(" space="); - DBG_PRINT(space_ * MICROS_PER_TICK); - DBG_PRINT(" ratio="); - DBG_PRINTLN(ratio_); - - if (MATCH_MARK(space_ + mark_, MAGIQUEST_PERIOD)) { - if (ratio_ > 1) { - // It's a 0 - data.llword <<= 1; -#if DEBUG - bitstring[(offset/2)-1] = '0'; -#endif - } else { - // It's a 1 - data.llword = (data.llword << 1) | 1; -#if DEBUG - bitstring[(offset/2)-1] = '1'; -#endif - } - } else { - DBG_PRINTLN("MATCH_MARK failed"); - return false; - } - } -#if DEBUG - DBG_PRINTLN(bitstring); -#endif - - // Success - results.decode_type = MAGIQUEST; - results.bits = offset / 2; - results.value = data.cmd.wand_id; - results.magnitude = data.cmd.magnitude; - - DBG_PRINT("MQ: bits="); - DBG_PRINT(results.bits); - DBG_PRINT(" value="); - DBG_PRINT(results.value); - DBG_PRINT(" magnitude="); - DBG_PRINTLN(results.magnitude); - - return true; -} -bool IRrecv::decodeMagiQuest(decode_results *aResults) { - bool aReturnValue = decodeMagiQuest(); - *aResults = results; - return aReturnValue; -} -#endif diff --git a/src/ir_MagiQuest.hpp b/src/ir_MagiQuest.hpp new file mode 100644 index 000000000..ccd214f89 --- /dev/null +++ b/src/ir_MagiQuest.hpp @@ -0,0 +1,250 @@ +/* + * ir_MagiQuest.hpp + * + * Contains functions for receiving and sending MagiQuest Protocol + * Based off the Magiquest fork of Arduino-IRremote by mpflaga https://github.com/mpflaga/Arduino-IRremote/ + * + * RESULT: + * The 31 bit wand ID is available in decodedRawData. + * The lower 16 bit of the ID is available in address. + * The magnitude is available in command. + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2017-2024 E. Stuart Hicks , Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_MAGIQUEST_HPP +#define _IR_MAGIQUEST_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif +// +//============================================================================== +// +// M M AA GGG III QQQ U U EEEE SSS TTTTTT +// MM MM A A G I Q Q U U E S TT +// M M M AAAA G GG I Q Q U U EEE SSS TT +// M M A A G G I Q QQ U U E S TT +// M M A A GGG III QQQQ UUU EEEE SSSS TT +// Q +//============================================================================== +/* + * https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp + * https://github.com/Arduino-IRremote/Arduino-IRremote/issues/1015#issuecomment-1222247231 + + Protocol=MagiQuest Address=0xFF00 Command=0x176 Raw-Data=0x6BCDFF00 56 bits MSB first + + 250,- 800 + 250,- 850 + 250,- 850 + 250,- 850 // 8 zero start bits + + 250,- 850 + 300,- 800 + 250,- 850 + 250,- 850 + + // 31 ID bits + + 550,- 600 + 550,- 550 + 350,- 800 + 600,- 600 // 110 1 6 + + 200,- 950 + 550,- 600 + 550,- 600 + 550,- 600 // 011 1 B - 1(from above)011 => B + + 550,- 600 + 250,- 900 + 300,- 850 + 550,- 600 // 100 1 C + + 550,- 600 + 300,- 850 + 550,- 600 + 550,- 600 + + 550,- 600 + 550,- 600 + 550,- 600 + 550,- 600 + + 550,- 600 + 550,- 600 + 550,- 600 + 300,- 800 + + 350,- 850 + 300,- 850 + 300,- 850 + 300,- 850 + + 300,- 850 + 300,- 850 + 300,- 850 + 550,- 600 // 000 1 - 3 LSB ID bits 000 + 1 MSB magnitude bit 1 + + // 8 bit magnitude + + 300,- 850 + 550,- 600 + 550,- 600 + 550,- 600 + + 300,- 850 + 550,- 600 + 550,- 600 + 250,- 900 + + // Checksum (+ sum of the 5 bytes before == 0) + + 250,- 900 + 300,- 900 + 250,- 850 + 550,- 600 + + 600,- 550 + 300,- 900 + 250,- 850 + 550 + + // No stop bit! + */ +// MSB first, 8 start bits (zero), 31 wand id bits, 9 magnitude bits 8 checksum bits and no stop bit => 56 bits +#define MAGIQUEST_CHECKSUM_BITS 8 // magiquest_t.cmd.checksum +#define MAGIQUEST_MAGNITUDE_BITS 9 // magiquest_t.cmd.magnitude +#define MAGIQUEST_WAND_ID_BITS 31 // magiquest_t.cmd.wand_id -> wand-id is handled as 32 bit and always even +#define MAGIQUEST_START_BITS 8 // magiquest_t.cmd.StartBits + +#define MAGIQUEST_PERIOD 1150 // Time for a full MagiQuest "bit" (1100 - 1200 usec) + +#define MAGIQUEST_DATA_BITS (MAGIQUEST_CHECKSUM_BITS + MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_WAND_ID_BITS) // 48 Size of the command without the start bits +#define MAGIQUEST_BITS (MAGIQUEST_CHECKSUM_BITS + MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_WAND_ID_BITS + MAGIQUEST_START_BITS) // 56 Size of the command with the start bits + +/* + * 0 = 25% mark & 75% space across 1 period + * 1150 * 0.25 = 288 usec mark + * 1150 - 288 = 862 usec space + * 1 = 50% mark & 50% space across 1 period + * 1150 * 0.5 = 575 usec mark + * 1150 - 575 = 575 usec space + */ +#define MAGIQUEST_UNIT (MAGIQUEST_PERIOD / 4) // 287.5 + +#define MAGIQUEST_ONE_MARK (2 * MAGIQUEST_UNIT) // 576 +#define MAGIQUEST_ONE_SPACE (2 * MAGIQUEST_UNIT) // 576 +#define MAGIQUEST_ZERO_MARK MAGIQUEST_UNIT // 287.5 +#define MAGIQUEST_ZERO_SPACE (3 * MAGIQUEST_UNIT) // 864 + +// assume 110 as repeat period +struct PulseDistanceWidthProtocolConstants const MagiQuestProtocolConstants PROGMEM = { MAGIQUEST, 38, MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE, +MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE, MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST | SUPPRESS_STOP_BIT, 110, +nullptr }; +//+============================================================================= +// +/** + * @param aWandId 31 bit ID + * @param aMagnitude 9 bit Magnitude + */ +void IRsend::sendMagiQuest(uint32_t aWandId, uint16_t aMagnitude) { + + // Set IR carrier frequency + enableIROut(38); + + aMagnitude &= 0x1FF; // we have 9 bit + LongUnion tWandId; + tWandId.ULong = aWandId << 1; + uint8_t tChecksum = (tWandId.Bytes[0]) + tWandId.Bytes[1] + tWandId.Bytes[2] + tWandId.Bytes[3]; + tChecksum += aMagnitude + (aMagnitude >> 8); + tChecksum = ~tChecksum + 1; + + // 8 start bits + sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, 0, 8); + // 48 bit data + sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, aWandId, MAGIQUEST_WAND_ID_BITS); // send only 31 bit, do not send MSB here + sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, aMagnitude, MAGIQUEST_MAGNITUDE_BITS); + sendPulseDistanceWidthData_P(&MagiQuestProtocolConstants, tChecksum, MAGIQUEST_CHECKSUM_BITS); +#if defined(LOCAL_DEBUG) + // must be after sending, in order not to destroy the send timing + Serial.print(F("MagiQuest checksum=0x")); + Serial.println(tChecksum, HEX); +#endif +} + +//+============================================================================= +// +/* + * decodes a 56 bit result, which is not really compatible with standard decoder layout + * magnitude is stored in command + * 31 bit wand_id is stored in decodedRawData + * lower 16 bit of wand_id is stored in address + */ +bool IRrecv::decodeMagiQuest() { + + // Check we have the right amount of data, magnitude and ID bits and 8 start bits + 0 stop bit + if (decodedIRData.rawlen != (2 * MAGIQUEST_BITS)) { + IR_DEBUG_PRINT(F("MagiQuest: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not 112")); + return false; + } + + /* + * Check for 8 zero header bits + */ + if (!decodePulseDistanceWidthData_P(&MagiQuestProtocolConstants, MAGIQUEST_START_BITS, 1)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("MagiQuest: ")); + Serial.println(F("Start bit decode failed")); +#endif + return false; + } + if (decodedIRData.decodedRawData != 0) { +#if defined(LOCAL_DEBUG) + Serial.print(F("MagiQuest: ")); + Serial.print(F("Not 8 leading zero start bits received, RawData=0x")); + Serial.println(decodedIRData.decodedRawData, HEX); +#endif + return false; + } + + /* + * Decode the 31 bit ID + */ + if (!decodePulseDistanceWidthData_P(&MagiQuestProtocolConstants, MAGIQUEST_WAND_ID_BITS, (MAGIQUEST_START_BITS * 2) + 1)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("MagiQuest: ")); + Serial.println(F("ID decode failed")); +#endif + return false; + } + LongUnion tDecodedRawData; +#if defined(LOCAL_DEBUG) + Serial.print(F("31 bit WandId=0x")); + Serial.println(decodedIRData.decodedRawData, HEX); +#endif + uint32_t tWandId = decodedIRData.decodedRawData; // save tWandId for later use + tDecodedRawData.ULong = decodedIRData.decodedRawData << 1; // shift for checksum computation + uint8_t tChecksum = tDecodedRawData.Bytes[0] + tDecodedRawData.Bytes[1] + tDecodedRawData.Bytes[2] + tDecodedRawData.Bytes[3]; +#if defined(LOCAL_DEBUG) + Serial.print(F("31 bit WandId=0x")); + Serial.print(decodedIRData.decodedRawData, HEX); + Serial.print(F(" shifted=0x")); + Serial.println(tDecodedRawData.ULong, HEX); +#endif + /* + * Decode the 9 bit Magnitude + 8 bit checksum + */ + if (!decodePulseDistanceWidthData_P(&MagiQuestProtocolConstants, MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_CHECKSUM_BITS, + ((MAGIQUEST_WAND_ID_BITS + MAGIQUEST_START_BITS) * 2) + 1)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("MagiQuest: ")); + Serial.println(F("Magnitude + checksum decode failed")); +#endif + return false; + } + +#if defined(LOCAL_DEBUG) + Serial.print(F("Magnitude + checksum=0x")); + Serial.println(decodedIRData.decodedRawData, HEX); +#endif + tDecodedRawData.ULong = decodedIRData.decodedRawData; + decodedIRData.command = tDecodedRawData.UBytes[1] | tDecodedRawData.UBytes[2] << 8; // Values observed are 0x102,01,04,37,05,38,2D| 02,06,04|03,103,12,18,0E|09 + + tChecksum += tDecodedRawData.UBytes[2] /* only one bit */+ tDecodedRawData.UBytes[1] + tDecodedRawData.UBytes[0]; + if (tChecksum != 0) { + decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED; +#if defined(LOCAL_DEBUG) + Serial.print(F("Checksum 0x")); + Serial.print(tChecksum, HEX); + Serial.println(F(" is not 0")); +#endif + } + + // Success + decodedIRData.decodedRawData = tWandId; // 31 bit wand_id + decodedIRData.address = tWandId; // lower 16 bit of wand_id + decodedIRData.extra = tWandId >> 16; // upper 15 bit of wand_id + + decodedIRData.protocol = MAGIQUEST; + decodedIRData.numberOfBits = MAGIQUEST_BITS; + decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; + + return true; +} +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_MAGIQUEST_HPP diff --git a/src/ir_NEC.cpp b/src/ir_NEC.cpp deleted file mode 100644 index 9549489cf..000000000 --- a/src/ir_NEC.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * ir_NEC.cpp - * - * Contains functions for receiving and sending NEC IR Protocol in "raw" and standard format with 16 bit Address 8bit Data - * - * This file is part of Arduino-IRremote https://github.com/z3t0/Arduino-IRremote. - * - */ - -#include "IRremote.h" - -//============================================================================== -// N N EEEEE CCCC -// NN N E C -// N N N EEE C -// N NN E C -// N N EEEEE CCCC -//============================================================================== - -#define NEC_BITS 32 -#define NEC_HEADER_MARK 9000 -#define NEC_HEADER_SPACE 4500 -#define NEC_BIT_MARK 560 -#define NEC_ONE_SPACE 1690 -#define NEC_ZERO_SPACE 560 -#define NEC_REPEAT_SPACE 2250 - -//+============================================================================= -#if SEND_NEC || SEND_NEC_STANDARD -/* - * Send repeat - * Repeat commands should be sent in a 110 ms raster. - */ -void IRsend::sendNECRepeat() { - enableIROut(38); - mark(NEC_HEADER_MARK); - space(NEC_REPEAT_SPACE); - mark(NEC_BIT_MARK); - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -#if SEND_NEC -/* - * Repeat commands should be sent in a 110 ms raster. - * https://www.sbprojects.net/knowledge/ir/nec.php - */ -void IRsend::sendNEC(uint32_t data, uint8_t nbits, bool repeat) { - // Set IR carrier frequency - enableIROut(38); - - if (data == REPEAT || repeat) { - sendNECRepeat(); - return; - } - - // Header - mark(NEC_HEADER_MARK); - space(NEC_HEADER_SPACE); - // Data - sendPulseDistanceWidthData(NEC_BIT_MARK, NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, data, nbits); - - // Stop bit - mark(NEC_BIT_MARK); - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -#if SEND_NEC_STANDARD -/* - * Repeat commands should be sent in a 110 ms raster. - * https://www.sbprojects.net/knowledge/ir/nec.php - */ -void IRsend::sendNECStandard(uint16_t aAddress, uint8_t aCommand, uint8_t aNumberOfRepeats) { - // Set IR carrier frequency - enableIROut(38); - - unsigned long tStartMillis = millis(); - // Header - mark(NEC_HEADER_MARK); - space(NEC_HEADER_SPACE); - // Address 16 bit LSB first - sendPulseDistanceWidthData(NEC_BIT_MARK, NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, aAddress, 16, false); - - // send 8 command bits and then 8 inverted command bits LSB first - uint16_t tCommand = ((~aCommand) << 8) | aCommand; - // Command 16 bit LSB first - sendPulseDistanceWidthData(NEC_BIT_MARK, NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, tCommand, 16, false); - mark(NEC_BIT_MARK); // Stop bit - space(0); // Always end with the LED off - - for (uint8_t i = 0; i < aNumberOfRepeats; ++i) { - // send repeat in a 110 ms raster - delay((tStartMillis + 110) - millis()); - tStartMillis = millis(); - // send repeat - sendNECRepeat(); - } -} -#endif -//+============================================================================= -// NECs have a repeat only 4 items long -// -#if DECODE_NEC -bool IRrecv::decodeNEC() { - int offset = 1; // Index in to results; Skip first space. - -// Check header "mark" - if (!MATCH_MARK(results.rawbuf[offset], NEC_HEADER_MARK)) { - return false; - } - offset++; - -// Check for repeat - if ((results.rawlen == 4) && MATCH_SPACE(results.rawbuf[offset], NEC_REPEAT_SPACE) - && MATCH_MARK(results.rawbuf[offset + 1], NEC_BIT_MARK)) { - results.bits = 0; - results.value = REPEAT; - results.isRepeat = true; - results.decode_type = NEC; - return true; - } - -// Check we have enough data - +3 for start bit mark and space + stop bit mark - if (results.rawlen <= (2 * NEC_BITS) + 3) { - return false; - } - -// Check header "space" - if (!MATCH_SPACE(results.rawbuf[offset], NEC_HEADER_SPACE)) { - return false; - } - offset++; - - if (!decodePulseDistanceData(NEC_BITS, offset, NEC_BIT_MARK, NEC_ONE_SPACE, NEC_ZERO_SPACE)) { - return false; - } - - // Stop bit - if (!MATCH_MARK(results.rawbuf[offset + (2 * NEC_BITS)], NEC_BIT_MARK)) { - DBG_PRINT("Stop bit verify failed"); - return false; - } - -// Success - results.bits = NEC_BITS; - results.decode_type = NEC; - - return true; -} - -bool IRrecv::decodeNEC(decode_results *aResults) { - bool aReturnValue = decodeNEC(); - *aResults = results; - return aReturnValue; -} -#endif - -//+============================================================================= -// NECs have a repeat only 4 items long -// -#if DECODE_NEC_STANDARD -bool IRrecv::decodeNECStandard() { - long data = 0; // We decode in to here; Start with nothing - int offset = 1; // Index in to results; Skip first space. - - // Check header "mark" - if (!MATCH_MARK(results.rawbuf[offset], NEC_HEADER_MARK)) { - return false; - } - offset++; - - // Check for repeat - if ((results.rawlen == 4) && MATCH_SPACE(results.rawbuf[offset], NEC_REPEAT_SPACE) - && MATCH_MARK(results.rawbuf[offset + 1], NEC_BIT_MARK)) { - results.isRepeat = true; - results.bits = 0; - return true; - } - - // Check we have enough data - +3 for start bit mark and space + stop bit mark - if (results.rawlen <= (2 * NEC_BITS) + 3) { - return false; - } - // Check header "space" - if (!MATCH_SPACE(results.rawbuf[offset], NEC_HEADER_SPACE)) { - return false; - } - offset++; - - data = decodePulseDistanceData(NEC_BITS, offset, NEC_BIT_MARK, NEC_ONE_SPACE, NEC_ZERO_SPACE, false); - - // Stop bit - if (!MATCH_MARK(results.rawbuf[offset + (2 * NEC_BITS)], NEC_BIT_MARK)) { - DBG_PRINT("Stop bit verify failed"); - return false; - } - - // Success - uint16_t tCommand = data >> 16; - uint8_t tCommandNotInverted = tCommand & 0xFF; - uint8_t tCommandInverted = tCommand >> 8; - // plausi check for command - if ((tCommandNotInverted ^ tCommandInverted) != 0xFF) { - return false; - } - - results.isRepeat = false; - results.value = tCommandNotInverted; - results.bits = NEC_BITS; - results.address = data & 0xFFFF; // first 16 bit - results.decode_type = NEC_STANDARD; - - return true; -} -#endif - diff --git a/src/ir_NEC.hpp b/src/ir_NEC.hpp new file mode 100644 index 000000000..5bf750fea --- /dev/null +++ b/src/ir_NEC.hpp @@ -0,0 +1,440 @@ +/* + * ir_NEC.hpp + * + * Contains functions for receiving and sending NEC IR Protocol in "raw" and standard format with 16 or 8 bit address and 8 bit command + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2023 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_NEC_HPP +#define _IR_NEC_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG // IR_DEBUG_PRINT is a real print function here. Add local debug output. +#else +//#define LOCAL_DEBUG // This enables debug output only for this file. IR_DEBUG_PRINT is still a void function here. +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +//============================================================================== +// N N EEEEE CCCC +// NN N E C +// N N N EEE C +// N NN E C +// N N EEEEE CCCC +//============================================================================== +/* + Protocol=NEC Address=0x4 Command=0x8 Raw-Data=0xF708FB04 32 bits LSB first + +8950,-4450 + + 600,- 500 + 650,- 500 + 600,-1650 + 600,- 550 + + 600,- 500 + 600,- 500 + 650,- 500 + 600,- 500 + + 650,-1650 + 600,-1600 + 650,- 500 + 600,-1650 + + 600,-1650 + 600,-1650 + 600,-1600 + 650,-1600 + + 650,- 500 + 600,- 550 + 600,- 500 + 600,-1650 + + 600,- 550 + 600,- 500 + 600,- 550 + 600,- 500 + + 600,-1650 + 600,-1650 + 600,-1650 + 600,- 550 + + 600,-1650 + 600,-1650 + 600,-1650 + 600,-1600 + + 650 + Sum: 68000 + + Protocol=NEC Address=0x8 Command=0x7 Repeat gap=40900us + rawData[4]: + -40900 + +10450,-2250 + + 700 + Sum: 13400 + */ +// http://www.hifi-remote.com/wiki/index.php/NEC +// https://www.sbprojects.net/knowledge/ir/nec.php +// NEC: LSB first, (or ) . +// ONKYO: like NEC but force to 16 independent address and 16 bit command bits: +// Standard NEC sends a special fixed repeat frame. +// NEC2: like NEC, but for repeat, the same full frame is sent after the 110 ms. I have a DVD remote with NEC2. +// NEC and NEC 2 only differ in the repeat frames, so the protocol can only be detected correctly after the first repeat. +// PIONEER (not implemented) is NEC2 with 40 kHz +// +// For Apple see https://en.wikipedia.org/wiki/Apple_Remote - <0x87EE:16> - not implemented! +// The parity is not implemented, so we get: <0x87EE:16> +// +// IRP notation: +// IRP: NEC {38.0k,564}<1,-1|1,-3>(16,-8,D:8,S:8,F:8,~F:8,1,^108m,(16,-4,1,^108m)*) ==> "*" means send special repeat frames o ore more times +// IRP: NEC2 {38.0k,564}<1,-1|1,-3>(16,-8,D:8,S:8,F:8,~F:8,1,^108m)+ ==> "+" means send frame 1 or more times (special repeat is missing here!) +// Interpretation of IRP notation: +// {38.0k,564} ==> 38.0k -> Frequency , 564 -> unit in microseconds (we use 560), no "msb", so "lsb" is assumed +// <1,-1|1,-3> ==> Zero is 1 unit mark and space | One is 1 unit mark and 3 units space +// 16,-8 ==> Start bit durations +// D:8,S:8,F:8,~F:8 ==> D:8 -> 8 bit bitfield for Device, S:8 -> 8 bit bitfield for Subdevice, F:8 -> 8 bit bitfield for Function, ~F:8 -> 8 bit inverted bitfield for Function +// 1,^108m ==> 1 -> unit mark Stop bit, ^108m -> wait until 108 milliseconds after start of protocol (we use 110) +// +#define NEC_ADDRESS_BITS 16 // 16 bit address or 8 bit address and 8 bit inverted address +#define NEC_COMMAND_BITS 16 // Command and inverted command + +#define NEC_BITS (NEC_ADDRESS_BITS + NEC_COMMAND_BITS) +#define NEC_UNIT 560 // 21.28 periods of 38 kHz, 11.2 ticks TICKS_LOW = 8.358 TICKS_HIGH = 15.0 + +#define NEC_HEADER_MARK (16 * NEC_UNIT) // 9000 | 180 +#define NEC_HEADER_SPACE (8 * NEC_UNIT) // 4500 | 90 + +#define NEC_BIT_MARK NEC_UNIT +#define NEC_ONE_SPACE (3 * NEC_UNIT) // 1690 | 33.8 TICKS_LOW = 25.07 TICKS_HIGH = 45.0 +#define NEC_ZERO_SPACE NEC_UNIT + +#define NEC_REPEAT_HEADER_SPACE (4 * NEC_UNIT) // 2250 + +#define NEC_AVERAGE_DURATION 62000 // NEC_HEADER_MARK + NEC_HEADER_SPACE + 32 * 2,5 * NEC_UNIT + NEC_UNIT // 2.5 because we assume more zeros than ones +#define NEC_MINIMAL_DURATION 49900 // NEC_HEADER_MARK + NEC_HEADER_SPACE + 32 * 2 * NEC_UNIT + NEC_UNIT // 2.5 because we assume more zeros than ones +#define NEC_REPEAT_DURATION (NEC_HEADER_MARK + NEC_REPEAT_HEADER_SPACE + NEC_BIT_MARK) // 12 ms +#define NEC_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down. +#define NEC_REPEAT_DISTANCE (NEC_REPEAT_PERIOD - NEC_AVERAGE_DURATION) // 48 ms +#define NEC_MAXIMUM_REPEAT_DISTANCE (NEC_REPEAT_PERIOD - NEC_MINIMAL_DURATION + 10000) // 70 ms + +#define APPLE_ADDRESS 0x87EE + +struct PulseDistanceWidthProtocolConstants const NECProtocolConstants PROGMEM = {NEC, NEC_KHZ, NEC_HEADER_MARK, NEC_HEADER_SPACE, NEC_BIT_MARK, + NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (NEC_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), + &sendNECSpecialRepeat}; + +// Like NEC but repeats are full frames instead of special NEC repeats +struct PulseDistanceWidthProtocolConstants const NEC2ProtocolConstants PROGMEM = {NEC2, NEC_KHZ, NEC_HEADER_MARK, NEC_HEADER_SPACE, NEC_BIT_MARK, + NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (NEC_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr}; + +/************************************ + * Start of send and decode functions + ************************************/ + +/** + * Send special NEC repeat frame + * Repeat commands should be sent in a 110 ms raster. + */ +void IRsend::sendNECRepeat() { + enableIROut (NEC_KHZ); // 38 kHz + mark(NEC_HEADER_MARK); // + 9000 + space(NEC_REPEAT_HEADER_SPACE); // - 2250 + mark(NEC_BIT_MARK); // + 560 +} + +/** + * Static function variant of IRsend::sendNECRepeat + * For use in ProtocolConstants. Saves up to 250 bytes compared to a member function. + */ +void sendNECSpecialRepeat() { + IrSender.enableIROut(NEC_KHZ); // 38 kHz + IrSender.mark(NEC_HEADER_MARK); // + 9000 + IrSender.space(NEC_REPEAT_HEADER_SPACE); // - 2250 + IrSender.mark(NEC_BIT_MARK); // + 560 +} + +/** + * Convert 16 bit address and 16 bit command to 32 bit NECRaw data + * If we get a command < 0x100, we send command and then ~command + * If we get an address < 0x100, we send 8 bit address and then ~address + * !!! Be aware, that this is flexible, but makes it impossible to send e.g. 0x0042 as 16 bit value!!! + * To force send 16 bit address, use: sendOnkyo(). + */ +uint32_t IRsend::computeNECRawDataAndChecksum(uint16_t aAddress, uint16_t aCommand) { + LongUnion tRawData; + + // Address 16 bit LSB first + if ((aAddress & 0xFF00) == 0) { + // assume 8 bit address -> send 8 address bits and then 8 inverted address bits LSB first + tRawData.UByte.LowByte = aAddress; + tRawData.UByte.MidLowByte = ~tRawData.UByte.LowByte; + } else { + tRawData.UWord.LowWord = aAddress; + } + + // send 8 command bits and then 8 inverted command bits LSB first + tRawData.UByte.MidHighByte = aCommand; + tRawData.UByte.HighByte = ~aCommand; + return tRawData.ULong; +} + +/** + * NEC Send frame and special repeats + * There is NO delay after the last sent repeat! + * @param aNumberOfRepeats If < 0 then only a special NEC repeat frame will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction(). + */ +void IRsend::sendNEC(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) { + sendPulseDistanceWidth_P(&NECProtocolConstants, computeNECRawDataAndChecksum(aAddress, aCommand), NEC_BITS, aNumberOfRepeats); +} + +/** + * There is NO delay after the last sent repeat! + * @param aNumberOfRepeats If < 0 then only a special repeat frame without leading and trailing space + * will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction(). + */ +void IRsend::sendOnkyo(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) { + sendPulseDistanceWidth_P(&NECProtocolConstants, (uint32_t) aCommand << 16 | aAddress, NEC_BITS, aNumberOfRepeats); +} + +/** + * NEC2 Send frame !!! and repeat the frame for each requested repeat !!! + * There is NO delay after the last sent repeat! + * @param aNumberOfRepeats If < 0 then nothing is sent. + */ +void IRsend::sendNEC2(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) { + sendPulseDistanceWidth_P(&NEC2ProtocolConstants, computeNECRawDataAndChecksum(aAddress, aCommand), NEC_BITS, aNumberOfRepeats); +} + +/** + * Apple: Send NEC with fixed 16 bit Apple address 0x87EE. + * There is NO delay after the last sent repeat! + * https://en.wikipedia.org/wiki/Apple_Remote + * https://gist.github.com/darconeous/4437f79a34e3b6441628 + * @param aNumberOfRepeats If < 0 then only a special repeat frame without leading and trailing space + * will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction(). + */ +void IRsend::sendApple(uint8_t aDeviceId, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + LongUnion tRawData; + + // Address 16 bit LSB first fixed value of 0x87EE + tRawData.UWord.LowWord = APPLE_ADDRESS; + + // send Apple code and then 8 command bits LSB first + tRawData.UByte.MidHighByte = aCommand; + tRawData.UByte.HighByte = aDeviceId; // e.g. 0xD7 + + sendPulseDistanceWidth_P(&NECProtocolConstants, tRawData.ULong, NEC_BITS, aNumberOfRepeats); +} + +/** + * Sends NEC protocol + * @param aNumberOfRepeats If < 0 then only a special repeat frame without leading and trailing space + * will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction(). + */ +void IRsend::sendNECRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats) { + sendPulseDistanceWidth_P(&NECProtocolConstants, aRawData, NEC_BITS, aNumberOfRepeats); +} + +/** + * Decodes also Onkyo and Apple + */ +bool IRrecv::decodeNEC() { + /* + * First check for right data length + * Next check start bit + * Next try the decode + */ + // Check we have the right amount of data (68). The +4 is for initial gap, start bit mark and space + stop bit mark. + if (decodedIRData.rawlen != ((2 * NEC_BITS) + 4) && (decodedIRData.rawlen != 4)) { + IR_DEBUG_PRINT(F("NEC: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not 68 or 4")); + return false; + } + + // Check header "mark" this must be done for repeat and data + if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], NEC_HEADER_MARK)) { + return false; + } + + // Check for repeat - here we have another header space length + if (decodedIRData.rawlen == 4) { + if (matchSpace(decodedIRData.rawDataPtr->rawbuf[2], NEC_REPEAT_HEADER_SPACE) + && matchMark(decodedIRData.rawDataPtr->rawbuf[3], NEC_BIT_MARK)) { + decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_LSB_FIRST; + decodedIRData.address = lastDecodedAddress; + decodedIRData.command = lastDecodedCommand; + decodedIRData.protocol = lastDecodedProtocol; + return true; + } + return false; + } + + // Check command header space + if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], NEC_HEADER_SPACE)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("NEC: ")); + Serial.println(F("Header space length is wrong")); +#endif + return false; + } + + // Try to decode as NEC protocol + if (!decodePulseDistanceWidthData_P(&NECProtocolConstants, NEC_BITS)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("NEC: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } + + // Success +// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value + LongUnion tValue; + tValue.ULong = decodedIRData.decodedRawData; + decodedIRData.command = tValue.UByte.MidHighByte; // 8 bit + +#if defined(DECODE_ONKYO) + // Here only Onkyo protocol is supported -> force 16 bit address and command decoding + decodedIRData.address = tValue.UWord.LowWord; // first 16 bit + decodedIRData.protocol = ONKYO; + decodedIRData.command = tValue.UWord.HighWord; // 16 bit command +#else + // Address + if (tValue.UWord.LowWord == APPLE_ADDRESS) { + /* + * Apple + */ + decodedIRData.protocol = APPLE; + decodedIRData.address = tValue.UByte.HighByte; + + } else { + /* + * NEC LSB first, so first sent bit is also LSB of decodedIRData.decodedRawData + */ + if (tValue.UByte.LowByte == (uint8_t)(~tValue.UByte.MidLowByte)) { + // standard 8 bit address NEC protocol + decodedIRData.address = tValue.UByte.LowByte; // first 8 bit + } else { + // extended NEC protocol + decodedIRData.address = tValue.UWord.LowWord; // first 16 bit + } + // Check for command if it is 8 bit NEC or 16 bit ONKYO + if (tValue.UByte.MidHighByte == (uint8_t)(~tValue.UByte.HighByte)) { + decodedIRData.protocol = NEC; + } else { + decodedIRData.protocol = ONKYO; + decodedIRData.command = tValue.UWord.HighWord; // 16 bit command + } + } +#endif + + decodedIRData.numberOfBits = NEC_BITS; + + // check for NEC2 repeat, do not check for same content ;-) + checkForRepeatSpaceTicksAndSetFlag(NEC_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK); + if (decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) { + decodedIRData.protocol = NEC2; + decodedIRData.flags |= IRDATA_FLAGS_IS_PROTOCOL_WITH_DIFFERENT_REPEAT; + } + return true; +} + +/********************************************************************************* + * Old deprecated functions, kept for backward compatibility to old 2.0 tutorials + *********************************************************************************/ + +bool IRrecv::decodeNECMSB(decode_results *aResults) { + unsigned int offset = 1; // Index in to results; Skip first space. + +// Check header "mark" + if (!matchMark(aResults->rawbuf[offset], NEC_HEADER_MARK)) { + return false; + } + offset++; + +// Check for repeat + if ((aResults->rawlen == 4) && matchSpace(aResults->rawbuf[offset], NEC_REPEAT_HEADER_SPACE) + && matchMark(aResults->rawbuf[offset + 1], NEC_BIT_MARK)) { + aResults->bits = 0; + aResults->value = 0xFFFFFFFF; + decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT; + decodedIRData.protocol = NEC; + return true; + } + + // Check we have the right amount of data (32). +4 for initial gap, start bit mark and space + stop bit mark + if (aResults->rawlen != (2 * NEC_BITS) + 4) { + IR_DEBUG_PRINT(F("NEC MSB: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(aResults->rawlen); + IR_DEBUG_PRINTLN(F(" is not 68")); + return false; + } + +// Check header "space" + if (!matchSpace(aResults->rawbuf[offset], NEC_HEADER_SPACE)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("NEC MSB: ")); + Serial.println(F("Header space length is wrong")); +#endif + return false; + } + offset++; + + if (!decodePulseDistanceWidthData(NEC_BITS, offset, NEC_BIT_MARK, NEC_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("NEC MSB: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } + + // Stop bit + if (!matchMark(aResults->rawbuf[offset + (2 * NEC_BITS)], NEC_BIT_MARK)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("NEC MSB: ")); + Serial.println(F("Stop bit mark length is wrong")); +#endif + return false; + } + +// Success + aResults->value = decodedIRData.decodedRawData; + aResults->bits = NEC_BITS; + aResults->decode_type = NEC; + decodedIRData.protocol = NEC; + + return true; +} + +/** + * With Send sendNECMSB() you can send your old 32 bit codes. + * To convert one into the other, you must reverse the byte positions and then reverse all bit positions of each byte. + * Use bitreverse32Bit(). + * Or write it as one binary string and reverse/mirror it. + * Example: + * 0xCB340102 byte reverse -> 02 01 34 CB bit reverse-> 40 80 2C D3. + * 0xCB340102 is binary 11001011001101000000000100000010. + * 0x40802CD3 is binary 01000000100000000010110011010011. + * If you read the first binary sequence backwards (right to left), you get the second sequence. + */ +void IRsend::sendNECMSB(uint32_t data, uint8_t nbits, bool repeat) { + // Set IR carrier frequency + enableIROut (NEC_KHZ); + + if (data == 0xFFFFFFFF || repeat) { + sendNECRepeat(); + return; + } + + // Header + mark(NEC_HEADER_MARK); + space(NEC_HEADER_SPACE); + + // Old version with MSB first Data + stop bit + sendPulseDistanceWidthData(NEC_BIT_MARK, NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, data, nbits, PROTOCOL_IS_MSB_FIRST); +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_NEC_HPP diff --git a/src/ir_Others.hpp b/src/ir_Others.hpp new file mode 100644 index 000000000..20e4cc62c --- /dev/null +++ b/src/ir_Others.hpp @@ -0,0 +1,113 @@ +/* + * ir_Others.hpp + * + * Contains functions for miscellaneous protocols + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ + +#ifndef _IR_OTHERS_HPP +#define _IR_OTHERS_HPP +//============================================================================== +// DDDD IIIII SSSS H H +// D D I S H H +// D D I SSS HHHHH +// D D I S H H +// DDDD IIIII SSSS H H +//============================================================================== + +// DISH support by Todd Treece +// +// The send function needs to be repeated 4 times +// Only send the last for characters of the hex. +// I.E. Use 0x1C10 instead of 0x0000000000001C10 as listed in the LIRC file. +// Here is the LIRC file I found that seems to match the remote codes from the +// oscilloscope: DISH NETWORK (echostar 301): +// http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx +#define DISH_BITS 16 +#define DISH_HEADER_MARK 400 +#define DISH_HEADER_SPACE 6100 +#define DISH_BIT_MARK 400 +#define DISH_ONE_SPACE 1700 +#define DISH_ZERO_SPACE 2800 +#define DISH_REPEAT_SPACE 6200 // really? + +struct PulseDistanceWidthProtocolConstants const DishProtocolConstants PROGMEM = { UNKNOWN, 56, DISH_HEADER_MARK, DISH_HEADER_SPACE, +DISH_BIT_MARK, DISH_ONE_SPACE, DISH_BIT_MARK, DISH_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, 40, nullptr }; + +void IRsend::sendDish(uint16_t aData) { + sendPulseDistanceWidth_P(&DishProtocolConstants, aData, DISH_BITS, 4); +} + +//============================================================================== +// W W H H Y Y N N TTTTT EEEEE RRRRR +// W W H H Y Y NN N T E R R +// W W W HHHHH Y N N N T EEE RRRR +// W W W H H Y N NN T E R R +// WWW H H Y N N T EEEEE R R +//============================================================================== +// Whynter A/C ARC-110WD added by Francesco Meschia +// see https://docs.google.com/spreadsheets/d/1dsr4Jh-nzC6xvSKGpLlPBF0NRwvlpyw-ozg8eZU813w/edit#gid=0 +// Looking at the code table the protocol is LSB first with start and stop bit. +// 4 bit checksum, constant address 0xAA00, 8 bit Command and 4 bit Command group +// but we use MSB first to be backwards compatible +#define WHYNTER_BITS 32 +#define WHYNTER_HEADER_MARK 2850 +#define WHYNTER_HEADER_SPACE 2850 +#define WHYNTER_BIT_MARK 750 +#define WHYNTER_ONE_SPACE 2150 +#define WHYNTER_ZERO_SPACE 750 + +struct PulseDistanceWidthProtocolConstants const WhynterProtocolConstants PROGMEM = { WHYNTER, 38, WHYNTER_HEADER_MARK, WHYNTER_HEADER_SPACE, +WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, WHYNTER_BIT_MARK, WHYNTER_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, 110, nullptr }; + +void IRsend::sendWhynter(uint32_t aData, uint8_t aNumberOfBitsToSend) { + sendPulseDistanceWidth_P(&WhynterProtocolConstants, aData, NEC_BITS, aNumberOfBitsToSend); +} + +bool IRrecv::decodeWhynter() { + // Check we have the right amount of data (68). The +4 is for initial gap, start bit mark and space + stop bit mark. + if (decodedIRData.rawlen != (2 * WHYNTER_BITS) + 4) { + return false; + } + if (!checkHeader_P(&WhynterProtocolConstants)) { + return false; + } + if (!decodePulseDistanceWidthData_P(&WhynterProtocolConstants, WHYNTER_BITS)) { + return false; + } + // Success + decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; + decodedIRData.numberOfBits = WHYNTER_BITS; + decodedIRData.protocol = WHYNTER; + return true; +} + +/** @}*/ +#endif // _IR_OTHERS_HPP diff --git a/src/ir_Panasonic.cpp b/src/ir_Panasonic.cpp deleted file mode 100644 index e86eef0ac..000000000 --- a/src/ir_Panasonic.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "IRremote.h" - -//============================================================================== -// PPPP AAA N N AAA SSSS OOO N N IIIII CCCC -// P P A A NN N A A S O O NN N I C -// PPPP AAAAA N N N AAAAA SSS O O N N N I C -// P A A N NN A A S O O N NN I C -// P A A N N A A SSSS OOO N N IIIII CCCC -//============================================================================== - -#define PANASONIC_ADDRESS_BITS 16 -#define PANASONIC_DATA_BITS 32 -#define PANASONIC_BITS (PANASONIC_ADDRESS_BITS + PANASONIC_DATA_BITS) -#define PANASONIC_HEADER_MARK 3502 -#define PANASONIC_HEADER_SPACE 1750 -#define PANASONIC_BIT_MARK 502 -#define PANASONIC_ONE_SPACE 1244 -#define PANASONIC_ZERO_SPACE 400 - -//+============================================================================= -#if SEND_PANASONIC -void IRsend::sendPanasonic(unsigned int address, unsigned long data) { - // Set IR carrier frequency - enableIROut(37); // 36.7kHz is the correct frequency - - // Header - mark(PANASONIC_HEADER_MARK); - space(PANASONIC_HEADER_SPACE); - - // Address - sendPulseDistanceWidthData(PANASONIC_BIT_MARK, PANASONIC_ONE_SPACE, PANASONIC_BIT_MARK, PANASONIC_ZERO_SPACE, address, - PANASONIC_ADDRESS_BITS); - - // Data - sendPulseDistanceWidthData(PANASONIC_BIT_MARK, PANASONIC_ONE_SPACE, PANASONIC_BIT_MARK, - PANASONIC_ZERO_SPACE, data, PANASONIC_DATA_BITS); - - // Footer - mark(PANASONIC_BIT_MARK); - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -#if DECODE_PANASONIC -bool IRrecv::decodePanasonic() { - int offset = 1; - - if (results.rawlen < (2 * PANASONIC_BITS) + 2) { - return false; - } - - if (!MATCH_MARK(results.rawbuf[offset], PANASONIC_HEADER_MARK)) { - return false; - } - offset++; - if (!MATCH_MARK(results.rawbuf[offset], PANASONIC_HEADER_SPACE)) { - return false; - } - offset++; - - // decode address - if (!decodePulseDistanceData(PANASONIC_ADDRESS_BITS, offset, PANASONIC_BIT_MARK, PANASONIC_ONE_SPACE, - PANASONIC_ZERO_SPACE)) { - return false; - } - results.address = results.value; - - if (!decodePulseDistanceData(PANASONIC_DATA_BITS, offset + PANASONIC_ADDRESS_BITS, PANASONIC_BIT_MARK, - PANASONIC_ONE_SPACE, PANASONIC_ZERO_SPACE)) { - return false; - } - - results.decode_type = PANASONIC; - results.bits = PANASONIC_BITS; - - return true; -} - -bool IRrecv::decodePanasonic(decode_results *aResults) { - bool aReturnValue = decodePanasonic(); - *aResults = results; - return aReturnValue; -} -#endif - diff --git a/src/ir_Pronto.hpp b/src/ir_Pronto.hpp new file mode 100644 index 000000000..5fbad18c5 --- /dev/null +++ b/src/ir_Pronto.hpp @@ -0,0 +1,346 @@ +/* + * @file ir_Pronto.hpp + * @brief In this file, the functions IRrecv::compensateAndPrintPronto and IRsend::sendPronto are defined. + * + * Pronto is the standard for the professional audio and video hardware market. + * + * See http://www.harctoolbox.org/Glossary.html#ProntoSemantics + * Pronto database http://www.remotecentral.com/search.htm + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020 Bengt Martensson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_PRONTO_HPP +#define _IR_PRONTO_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ + +//! @cond +// DO NOT EXPORT from this file +static const uint16_t learnedToken = 0x0000U; +static const uint16_t learnedNonModulatedToken = 0x0100U; +static const uint16_t bitsInHexadecimal = 4U; +static const uint16_t digitsInProntoNumber = 4U; +static const uint16_t numbersInPreamble = 4U; +static const uint16_t hexMask = 0xFU; +static const uint32_t referenceFrequency = 4145146UL; +static const uint16_t fallbackFrequency = 64767U; // To use with frequency = 0; +static const uint32_t microsecondsInSeconds = 1000000UL; +static const uint16_t PRONTO_DEFAULT_GAP = 45000; +//! @endcond + +static uint16_t toFrequencyKHz(uint16_t code) { + return ((referenceFrequency / code) + 500) / 1000; +} + +/* + * Parse the string given as Pronto Hex, and send it a number of times given as argument. + * The first number denotes the type of the signal. 0000 denotes a raw IR signal with modulation, + // The second number denotes a frequency code + */ +void IRsend::sendPronto(const uint16_t *data, uint16_t length, int_fast8_t aNumberOfRepeats) { + uint16_t timebase = (microsecondsInSeconds * data[1] + referenceFrequency / 2) / referenceFrequency; + uint16_t khz; + switch (data[0]) { + case learnedToken: // normal, "learned" + khz = toFrequencyKHz(data[1]); + break; + case learnedNonModulatedToken: // non-demodulated, "learned" + khz = 0U; + break; + default: + return; // There are other types, but they are not handled yet. + } + uint16_t intros = 2 * data[2]; + uint16_t repeats = 2 * data[3]; +#if defined(LOCAL_DEBUG) + Serial.print(F("sendPronto intros=")); + Serial.print(intros); + Serial.print(F(" repeats=")); + Serial.println(repeats); +#endif + if (numbersInPreamble + intros + repeats != length) { // inconsistent sizes + return; + } + + /* + * Generate a new microseconds timing array for sendRaw. + * If recorded by IRremote, intro contains the whole IR data and repeat is empty + */ + uint16_t durations[intros + repeats]; + for (uint16_t i = 0; i < intros + repeats; i++) { + uint32_t duration = ((uint32_t) data[i + numbersInPreamble]) * timebase; + durations[i] = (uint16_t) ((duration <= UINT16_MAX) ? duration : UINT16_MAX); + } + + /* + * Send the intro. intros is even. + * Do not send the trailing space here, send it if repeats are requested + */ + if (intros >= 2) { + sendRaw(durations, intros - 1, khz); + } + + if (repeats == 0 || aNumberOfRepeats == 0) { + // only send intro once + return; + } + + /* + * Now send the trailing space/gap of the intro and all the repeats + */ + if (intros >= 2) { + delay(durations[intros - 1] / MICROS_IN_ONE_MILLI); // equivalent to space(durations[intros - 1]); but allow bigger values for the gap + } + for (int i = 0; i < aNumberOfRepeats; i++) { + sendRaw(durations + intros, repeats - 1, khz); + if ((i + 1) < aNumberOfRepeats) { // skip last trailing space/gap, see above + delay(durations[intros + repeats - 1] / MICROS_IN_ONE_MILLI); + } + } +} + +/** + * Parse the string given as Pronto Hex, and send it a number of times given + * as the second argument. Thereby the division of the Pronto Hex into + * an intro-sequence and a repeat sequence is taken into account: + * First the intro sequence is sent, then the repeat sequence is sent times-1 times. + * However, if the intro sequence is empty, the repeat sequence is sent times times. + * Reference. + * + * Note: Using this function is very wasteful for the memory consumption on + * a small board. + * Normally it is a much better idea to use a tool like e.g. IrScrutinizer + * to transform Pronto type signals offline + * to a more memory efficient format. + * + * @param str C type string (null terminated) containing a Pronto Hex representation. + * @param aNumberOfRepeats Number of times to send the signal. + */ +void IRsend::sendPronto(const char *str, int_fast8_t aNumberOfRepeats) { + size_t len = strlen(str) / (digitsInProntoNumber + 1) + 1; + uint16_t data[len]; + const char *p = str; + char *endptr[1]; + for (uint16_t i = 0; i < len; i++) { + long x = strtol(p, endptr, 16); + if (x == 0 && i >= numbersInPreamble) { + // Alignment error?, bail immediately (often right result). + len = i; + break; + } + data[i] = static_cast(x); // If input is conforming, there can be no overflow! + p = *endptr; + } + sendPronto(data, len, aNumberOfRepeats); +} + +#if defined(__AVR__) +/** + * Version of sendPronto that reads from PROGMEM, saving RAM memory. + * @param str pronto C type string (null terminated) containing a Pronto Hex representation. + * @param aNumberOfRepeats Number of times to send the signal. + */ +//far pointer (? for ATMega2560 etc.) +void IRsend::sendPronto_PF(uint_farptr_t str, int_fast8_t aNumberOfRepeats) { + size_t len = strlen_PF(str); + char work[len + 1]; + strcpy_PF(work, str); // We know that string including terminating character fits in work + sendPronto(work, aNumberOfRepeats); +} + +//standard pointer +void IRsend::sendPronto_P(const char *str, int_fast8_t aNumberOfRepeats) { + size_t len = strlen_P(str); + char work[len + 1]; + strcpy_P(work, str); + sendPronto(work, aNumberOfRepeats); +} +#endif + +/* + * Copy flash data to ram buffer in stack + */ +void IRsend::sendPronto(const __FlashStringHelper *str, int_fast8_t aNumberOfRepeats) { + size_t len = strlen_P(reinterpret_cast(str)); + char work[len + 1]; + strcpy_P(work, reinterpret_cast(str)); + return sendPronto(work, aNumberOfRepeats); +} + +static uint16_t effectiveFrequency(uint16_t frequency) { + return frequency > 0 ? frequency : fallbackFrequency; +} + +static uint16_t toTimebase(uint16_t frequency) { + return microsecondsInSeconds / effectiveFrequency(frequency); +} + +static uint16_t toFrequencyCode(uint16_t frequency) { + return referenceFrequency / effectiveFrequency(frequency); +} + +static char hexDigit(uint16_t x) { + return (char) (x <= 9 ? ('0' + x) : ('A' + (x - 10))); +} + +static void dumpDigit(Print *aSerial, uint16_t number) { + aSerial->print(hexDigit(number)); +} + +static void dumpNumber(Print *aSerial, uint16_t number) { + for (uint16_t i = 0; i < digitsInProntoNumber; i++) { + uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i); + dumpDigit(aSerial, (number >> shifts) & hexMask); + } + aSerial->print(' '); +} + +static void dumpDuration(Print *aSerial, uint32_t duration, uint16_t timebase) { + dumpNumber(aSerial, (duration + timebase / 2) / timebase); +} + +/* + * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding! + */ +static void compensateAndDumpSequence(Print *aSerial, const volatile IRRawbufType *data, size_t length, uint16_t timebase) { + for (size_t i = 0; i < length; i++) { + uint32_t tDuration = data[i] * MICROS_PER_TICK; + if (i & 1) { + // Mark + tDuration -= getMarkExcessMicros(); + } else { + tDuration += getMarkExcessMicros(); + } + dumpDuration(aSerial, tDuration, timebase); + } + + // append a gap + dumpDuration(aSerial, PRONTO_DEFAULT_GAP, timebase); +} + +/** + * Print the result (second argument) as Pronto Hex on the Print supplied as argument. + * Used in the ReceiveDump example. + * @param aSerial The Print object on which to write, for Arduino you can use &Serial. + * @param aFrequencyHertz Modulation frequency in Hz. Often 38000Hz. + */ +void IRrecv::compensateAndPrintIRResultAsPronto(Print *aSerial, uint16_t aFrequencyHertz) { + aSerial->println(F("Pronto Hex as string")); + aSerial->print(F("char prontoData[] = \"")); + dumpNumber(aSerial, aFrequencyHertz > 0 ? learnedToken : learnedNonModulatedToken); + dumpNumber(aSerial, toFrequencyCode(aFrequencyHertz)); + dumpNumber(aSerial, (decodedIRData.rawlen + 1) / 2); + dumpNumber(aSerial, 0); + uint16_t timebase = toTimebase(aFrequencyHertz); + compensateAndDumpSequence(aSerial, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawlen - 1, timebase); // skip leading space + aSerial->println(F("\";")); +} + +/* + * Functions for dumping Pronto to a String. This is not very time and space efficient + * and can lead to resource problems especially on small processors like AVR's + */ + +static bool dumpDigit(String *aString, uint16_t number) { + aString->concat(hexDigit(number)); + return number; +} + +static size_t dumpNumber(String *aString, uint16_t number) { + + size_t size = 0; + + for (uint16_t i = 0; i < digitsInProntoNumber; i++) { + uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i); + size += dumpDigit(aString, (number >> shifts) & hexMask); + } + aString->concat(' '); + size++; + + return size; +} + +/* + * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding! + */ +static size_t dumpDuration(String *aString, uint32_t duration, uint16_t timebase) { + return dumpNumber(aString, (duration + timebase / 2) / timebase); +} + +static size_t compensateAndDumpSequence(String *aString, const volatile IRRawbufType *data, size_t length, uint16_t timebase) { + + size_t size = 0; + + for (size_t i = 0; i < length; i++) { + uint32_t tDuration = data[i] * MICROS_PER_TICK; + if (i & 1) { + // Mark + tDuration -= getMarkExcessMicros(); + } else { + tDuration += getMarkExcessMicros(); + } + size += dumpDuration(aString, tDuration, timebase); + } + + // append minimum gap + size += dumpDuration(aString, PRONTO_DEFAULT_GAP, timebase); + + return size; +} + +/* + * Writes Pronto HEX to a String object. + * Returns the amount of characters added to the string.(360 characters for a NEC code!) + */ +size_t IRrecv::compensateAndStorePronto(String *aString, uint16_t frequency) { + + size_t size = 0; + uint16_t timebase = toTimebase(frequency); + + size += dumpNumber(aString, frequency > 0 ? learnedToken : learnedNonModulatedToken); + size += dumpNumber(aString, toFrequencyCode(frequency)); + size += dumpNumber(aString, (decodedIRData.rawlen + 1) / 2); + size += dumpNumber(aString, 0); + size += compensateAndDumpSequence(aString, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawlen - 1, + timebase); // skip leading space + + return size; +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_PRONTO_HPP diff --git a/src/ir_RC5_RC6.cpp b/src/ir_RC5_RC6.cpp deleted file mode 100644 index 599a02413..000000000 --- a/src/ir_RC5_RC6.cpp +++ /dev/null @@ -1,350 +0,0 @@ -#include "IRremote.h" - -//+============================================================================= -// Gets one undecoded level at a time from the raw buffer. -// The RC5/6 decoding is easier if the data is broken into time intervals. -// E.g. if the buffer has MARK for 2 time intervals and SPACE for 1, -// successive calls to getRClevel will return MARK, MARK, SPACE. -// offset and used are updated to keep track of the current position. -// t1 is the time interval for a single bit in microseconds. -// Returns -1 for error (measured time interval is not a multiple of t1). -// -#if (DECODE_RC5 || DECODE_RC6) -int getRClevel(decode_results *results, unsigned int *offset, int *used, int t1) { - int width; - int val; - int correction; - int avail; - - if (*offset >= results->rawlen) { - return SPACE; // After end of recorded buffer, assume SPACE. - } - width = results->rawbuf[*offset]; - val = ((*offset) % 2) ? MARK : SPACE; - correction = (val == MARK) ? MARK_EXCESS_MICROS : - MARK_EXCESS_MICROS; - - if (MATCH(width, (t1) + correction)) { - avail = 1; - } else if (MATCH(width, (2 * t1) + correction)) { - avail = 2; - } else if (MATCH(width, (3 * t1) + correction)) { - avail = 3; - } else { - return -1; - } - - (*used)++; - if (*used >= avail) { - *used = 0; - (*offset)++; - } - - DBG_PRINTLN((val == MARK) ? "MARK" : "SPACE"); - - return val; -} -#endif - -//============================================================================== -// RRRR CCCC 55555 -// R R C 5 -// RRRR C 5555 -// R R C 5 -// R R CCCC 5555 -// -// NB: First bit must be a one (start bit) -// -#define MIN_RC5_SAMPLES 11 -#define RC5_T1 889 -#define RC5_RPT_LENGTH 46000 // Not used. - -//+============================================================================= -#if SEND_RC5 -void IRsend::sendRC5(uint32_t data, uint8_t nbits) { - // Set IR carrier frequency - enableIROut(36); - - // Start - mark(RC5_T1); - space(RC5_T1); - mark(RC5_T1); - - // Data - Biphase code MSB first - for (uint32_t mask = 1UL << (nbits - 1); mask; mask >>= 1) { - if (data & mask) { - space(RC5_T1); // 1 is space, then mark - mark(RC5_T1); - } else { - mark(RC5_T1); - space(RC5_T1); - } - } - - space(0); // Always end with the LED off -} - -void IRsend::sendRC5ext(uint8_t addr, uint8_t cmd, boolean toggle) { - // Set IR carrier frequency - enableIROut(36); - - uint8_t addressBits = 5; - uint8_t commandBits = 7; -// unsigned long nbits = addressBits + commandBits; - - // Start - mark(RC5_T1); - - // Bit #6 of the command part, but inverted! - uint8_t cmdBit6 = (1UL << (commandBits - 1)) & cmd; - if (cmdBit6) { - // Inverted (1 -> 0 = mark-to-space) - mark(RC5_T1); - space(RC5_T1); - } else { - space(RC5_T1); - mark(RC5_T1); - } - commandBits--; - - // Toggle bit - static int toggleBit = 1; - if (toggle) { - if (toggleBit == 0) { - toggleBit = 1; - } else { - toggleBit = 0; - } - } - if (toggleBit) { - space(RC5_T1); - mark(RC5_T1); - } else { - mark(RC5_T1); - space(RC5_T1); - } - - // Address - for (uint8_t mask = 1UL << (addressBits - 1); mask; mask >>= 1) { - if (addr & mask) { - space(RC5_T1); // 1 is space, then mark - mark(RC5_T1); - } else { - mark(RC5_T1); - space(RC5_T1); - } - } - - // Command - for (uint8_t mask = 1UL << (commandBits - 1); mask; mask >>= 1) { - if (cmd & mask) { - space(RC5_T1); // 1 is space, then mark - mark(RC5_T1); - } else { - mark(RC5_T1); - space(RC5_T1); - } - } - - space(0); // Always end with the LED off -} - -#endif - -//+============================================================================= -#if DECODE_RC5 -bool IRrecv::decodeRC5() { - int nbits; - long data = 0; - int used = 0; - unsigned int offset = 1; // Skip gap space - - if (results.rawlen < MIN_RC5_SAMPLES + 2) { - return false; - } - - // Get start bits - if (getRClevel(&results, &offset, &used, RC5_T1) != MARK) { - return false; - } - if (getRClevel(&results, &offset, &used, RC5_T1) != SPACE) { - return false; - } - if (getRClevel(&results, &offset, &used, RC5_T1) != MARK) { - return false; - } - - /* - * Get data bits - MSB first - */ - for (nbits = 0; offset < results.rawlen; nbits++) { - int levelA = getRClevel(&results, &offset, &used, RC5_T1); - int levelB = getRClevel(&results, &offset, &used, RC5_T1); - - if ((levelA == SPACE) && (levelB == MARK)) { - data = (data << 1) | 1; - } else if ((levelA == MARK) && (levelB == SPACE)) { - data = (data << 1) | 0; - } else { - return false; - } - } - - // Success - results.bits = nbits; - results.value = data; - results.decode_type = RC5; - return true; -} -bool IRrecv::decodeRC5(decode_results *aResults) { - bool aReturnValue = decodeRC5(); - *aResults = results; - return aReturnValue; -} -#endif - -//+============================================================================= -// RRRR CCCC 6666 -// R R C 6 -// RRRR C 6666 -// R R C 6 6 -// R R CCCC 666 -// -// NB : Caller needs to take care of flipping the toggle bit -// -#define MIN_RC6_SAMPLES 1 -#define RC6_HEADER_MARK 2666 -#define RC6_HEADER_SPACE 889 -#define RC6_T1 444 -#define RC6_RPT_LENGTH 46000 - -#if SEND_RC6 -void IRsend::sendRC6(uint32_t data, uint8_t nbits) { - // Set IR carrier frequency - enableIROut(36); - - // Header - mark(RC6_HEADER_MARK); - space(RC6_HEADER_SPACE); - - // Start bit - mark(RC6_T1); - space(RC6_T1); - - // Data - uint32_t mask = 1UL << (nbits - 1); - for (uint8_t i = 1; mask; i++, mask >>= 1) { - // The fourth bit we send is a "double width trailer bit" - int t = (i == 4) ? (RC6_T1 * 2) : (RC6_T1); - if (data & mask) { - mark(t); - space(t); - } else { - space(t); - mark(t); - } - } - - space(0); // Always end with the LED off -} - -void IRsend::sendRC6(uint64_t data, uint8_t nbits) { - // Set IR carrier frequency - enableIROut(36); - - // Header - mark(RC6_HEADER_MARK); - space(RC6_HEADER_SPACE); - - // Start bit - mark(RC6_T1); - space(RC6_T1); - - // Data - uint64_t mask = 1ULL << (nbits - 1); - for (uint8_t i = 1; mask; i++, mask >>= 1) { - // The fourth bit we send is a "double width trailer bit" - int t = (i == 4) ? (RC6_T1 * 2) : (RC6_T1); - if (data & mask) { - mark(t); - space(t); - } else { - space(t); - mark(t); - } - } - - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -#if DECODE_RC6 -bool IRrecv::decodeRC6() { - int nbits; - uint32_t data = 0; - int used = 0; - unsigned int offset = 1; // Skip first space - - if (results.rawlen < MIN_RC6_SAMPLES) { - return false; - } - -// Initial mark - if (!MATCH_MARK(results.rawbuf[offset], RC6_HEADER_MARK)) { - return false; - } - offset++; - - if (!MATCH_SPACE(results.rawbuf[offset], RC6_HEADER_SPACE)) { - return false; - } - offset++; - -// Get start bit (1) - if (getRClevel(&results, &offset, &used, RC6_T1) != MARK) { - return false; - } - if (getRClevel(&results, &offset, &used, RC6_T1) != SPACE) { - return false; - } - - for (nbits = 0; offset < results.rawlen; nbits++) { - int levelA, levelB; // Next two levels - - levelA = getRClevel(&results, &offset, &used, RC6_T1); - if (nbits == 3) { - // T bit is double wide; make sure second half matches - if (levelA != getRClevel(&results, &offset, &used, RC6_T1)) { - return false; - } - } - - levelB = getRClevel(&results, &offset, &used, RC6_T1); - if (nbits == 3) { - // T bit is double wide; make sure second half matches - if (levelB != getRClevel(&results, &offset, &used, RC6_T1)) { - return false; - } - } - - if ((levelA == MARK) && (levelB == SPACE)) { - data = (data << 1) | 1; // inverted compared to RC5 - } else if ((levelA == SPACE) && (levelB == MARK)) { - data = (data << 1) | 0; - } else { - return false; // Error - } - } - - // Success - results.bits = nbits; - results.value = data; - results.decode_type = RC6; - return true; -} -bool IRrecv::decodeRC6(decode_results *aResults) { - bool aReturnValue = decodeRC6(); - *aResults = results; - return aReturnValue; -} -#endif diff --git a/src/ir_RC5_RC6.hpp b/src/ir_RC5_RC6.hpp new file mode 100644 index 000000000..b8eebb57a --- /dev/null +++ b/src/ir_RC5_RC6.hpp @@ -0,0 +1,671 @@ +/* + * ir_RC5_RC6.hpp + * + * Contains functions for receiving and sending RC5, RC5X, RC6 protocols + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2024 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_RC5_RC6_HPP +#define _IR_RC5_RC6_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +uint8_t sLastSendToggleValue = 1; // To start first command with toggle 0 +//uint8_t sLastReceiveToggleValue = 3; // 3 -> start value + +//============================================================================== +// RRRR CCCC 55555 +// R R C 5 +// RRRR C 5555 +// R R C 5 +// R R CCCC 5555 +//============================================================================== +/* + Protocol=RC5 Address=0x11 Command=0x36 Raw-Data=0x1476 13 bits MSB first + + 900,- 900 + +1800,-1750 +1800,- 850 + 900,- 850 + 900,-1750 + + 950,- 850 + 900,- 850 +1800,-1750 + 950,- 850 + +1800 + Sum: 23100 + + RC5X with 7.th MSB of command set + Protocol=RC5 Address=0x11 Command=0x76 Toggle=1 Raw-Data=0xC76 13 bits MSB first + +1800,-1750 + + 850,- 900 +1800,- 850 + 950,- 850 + 900,-1750 + + 900,- 850 + 950,- 850 +1800,-1750 + 900,- 850 + +1800 + Sum: 23050 + */ +// +// see: https://www.sbprojects.net/knowledge/ir/rc5.php +// https://en.wikipedia.org/wiki/Manchester_code +// https://en.wikipedia.org/wiki/RC-5 +// https://forum.arduino.cc/t/sending-rc-5-extended-code-using-irsender/1045841/10 - Protocol Maranz Extended +// mark->space => 0 +// space->mark => 1 +// MSB first 1 start bit, 1 field bit, 1 toggle bit + 5 bit address + 6 bit command, no stop bit +// Field bit is 1 for RC5 and inverted 7. command bit for RC5X. That way the first 64 commands of RC5X remain compatible with the original RC5. +// SF TAAA AACC CCCC +// IR duty factor is 25%, +// +#define RC5_ADDRESS_BITS 5 +#define RC5_COMMAND_BITS 6 +#define RC5_COMMAND_FIELD_BIT 1 +#define RC5_TOGGLE_BIT 1 + +#define RC5_BITS (RC5_COMMAND_FIELD_BIT + RC5_TOGGLE_BIT + RC5_ADDRESS_BITS + RC5_COMMAND_BITS) // 13 + +#define RC5_UNIT 889 // 32 periods of 36 kHz (888.8888) + +#define MIN_RC5_MARKS ((RC5_BITS + 1) / 2) // 7 - Divided by 2 to handle the bit sequence of 01010101 which gives one mark and space for each 2 bits + +#define RC5_DURATION (15L * RC5_UNIT) // 13335 +#define RC5_REPEAT_PERIOD (128L * RC5_UNIT) // 113792 +#define RC5_REPEAT_DISTANCE (RC5_REPEAT_PERIOD - RC5_DURATION) // 100 ms +#define RC5_MAXIMUM_REPEAT_DISTANCE (RC5_REPEAT_DISTANCE + (RC5_REPEAT_DISTANCE / 4)) // Just a guess + +/************************************ + * Start of send and decode functions + ************************************/ + +/** + * @param aCommand If aCommand is >=0x40 then we switch automatically to RC5X. + * @param aEnableAutomaticToggle Send toggle bit according to the state of the static sLastSendToggleValue variable. + */ +void IRsend::sendRC5(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle) { + // Set IR carrier frequency + enableIROut (RC5_RC6_KHZ); + + uint16_t tIRData = ((aAddress & 0x1F) << RC5_COMMAND_BITS); + + if (aCommand < 0x40) { + // Auto discovery of RC5X, set field bit to 1 + tIRData |= 1 << (RC5_TOGGLE_BIT + RC5_ADDRESS_BITS + RC5_COMMAND_BITS); + } else { + // Mask bit 7 of command and let field bit 0 + aCommand &= 0x3F; + } + tIRData |= aCommand; + + if (aEnableAutomaticToggle) { + if (sLastSendToggleValue == 0) { + sLastSendToggleValue = 1; + // set toggled bit + tIRData |= 1 << (RC5_ADDRESS_BITS + RC5_COMMAND_BITS); + } else { + sLastSendToggleValue = 0; + } + } + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + + // start bit is sent by sendBiphaseData + sendBiphaseData(RC5_UNIT, tIRData, RC5_BITS); + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + // send repeated command in a fixed raster + delay(RC5_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI); + } + } +} + +/** + * Try to decode data as RC5 protocol + * _ _ _ _ _ _ _ _ _ _ _ _ _ + * Clock _____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| | + * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ End of each data bit period + * _ _ - Mark + * 2 Start bits for RC5 _____| |_| ... - Data starts with a space->mark bit + * - Space + * _ + * 1 Start bit for RC5X _____| ... + * + */ +bool IRrecv::decodeRC5() { + uint8_t tBitIndex; + uint32_t tDecodedRawData = 0; + + // Set Biphase decoding start values + initBiphaselevel(1, RC5_UNIT); // Skip gap space + + // Check we have the right amount of data (11 to 26). The +2 is for initial gap and start bit mark. + if (decodedIRData.rawlen < ((RC5_BITS + 1) / 2) + 2 && (RC5_BITS + 2) < decodedIRData.rawlen) { + // no debug output, since this check is mainly to determine the received protocol + IR_DEBUG_PRINT(F("RC5: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not between 9 and 15")); + return false; + } + +// Check start bit, the first space is included in the gap + if (getBiphaselevel() != MARK) { + IR_DEBUG_PRINT(F("RC5: ")); + IR_DEBUG_PRINTLN(F("first getBiphaselevel() is not MARK")); + return false; + } + + /* + * Get data bits - MSB first + */ + for (tBitIndex = 0; sBiphaseDecodeRawbuffOffset < decodedIRData.rawlen; tBitIndex++) { + // get next 2 levels and check for transition + uint8_t tStartLevel = getBiphaselevel(); + uint8_t tEndLevel = getBiphaselevel(); + + if ((tStartLevel == SPACE) && (tEndLevel == MARK)) { + // we have a space to mark transition here + tDecodedRawData = (tDecodedRawData << 1) | 1; + } else if ((tStartLevel == MARK) && (tEndLevel == SPACE)) { + // we have a mark to space transition here + tDecodedRawData = (tDecodedRawData << 1) | 0; + } else { +#if defined(LOCAL_DEBUG) + Serial.print(F("RC5: ")); + Serial.println(F("no transition found, decode failed")); +#endif + return false; + } + } + + // Success + decodedIRData.numberOfBits = tBitIndex; // must be RC5_BITS + + LongUnion tValue; + tValue.ULong = tDecodedRawData; + decodedIRData.decodedRawData = tDecodedRawData; + decodedIRData.command = tValue.UByte.LowByte & 0x3F; + decodedIRData.address = (tValue.UWord.LowWord >> RC5_COMMAND_BITS) & 0x1F; + + // Get the inverted 7. command bit for RC5X, the inverted value is always 1 for RC5 and serves as a second start bit. + if ((tValue.UWord.LowWord & (1 << (RC5_TOGGLE_BIT + RC5_ADDRESS_BITS + RC5_COMMAND_BITS))) == 0) { + decodedIRData.command += 0x40; + } + + decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; + if (tValue.UByte.MidLowByte & 0x8) { + decodedIRData.flags = IRDATA_FLAGS_TOGGLE_BIT | IRDATA_FLAGS_IS_MSB_FIRST; + } + decodedIRData.protocol = RC5; + + // check for repeat + checkForRepeatSpaceTicksAndSetFlag(RC5_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK); + + return true; +} + +//+============================================================================= +// RRRR CCCC 6666 +// R R C 6 +// RRRR C 6666 +// R R C 6 6 +// R R CCCC 666 +//+============================================================================= +// +/* + Protocol=RC6 Address=0xF1 Command=0x76 Raw-Data=0xF176 20 bits MSB first + +2650,- 850 + + 500,- 850 + 500,- 400 + 450,- 450 + 450,- 850 + +1400,- 400 + 450,- 450 + 450,- 450 + 450,- 900 + + 450,- 450 + 450,- 400 + 950,- 850 + 900,- 450 + + 450,- 450 + 450,- 850 + 950,- 400 + 450,- 900 + + 450 + Sum: 23150 + */ +// Frame RC6: 1 start bit + 1 Bit "1" + 3 mode bits (000) + 1 toggle bit + 8 address + 8 command bits + 2666us pause - 22 bits incl. start bit +// Frame RC6A: 1 start bit + 1 Bit "1" + 3 mode bits (110) + 1 toggle bit + "1" + 14 customer bits + 8 system bits + 8 command bits + 2666us pause - 37 bits incl. start bit +// !!! toggle bit has another timing :-( !!! +// mark->space => 1 +// space->mark => 0 +// https://www.sbprojects.net/knowledge/ir/rc6.php +// https://www.mikrocontroller.net/articles/IRMP_-_english#RC6_.2B_RC6A +// https://en.wikipedia.org/wiki/Manchester_code +#define MIN_RC6_SAMPLES 1 + +#define RC6_RPT_LENGTH 46000 + +#define RC6_LEADING_BIT 1 +#define RC6_MODE_BITS 3 // never seen others than all 0 for Philips TV +#define RC6_TOGGLE_BIT 1 // toggles at every key press. Can be used to distinguish repeats from 2 key presses and has another timing :-(. +#define RC6_TOGGLE_BIT_INDEX RC6_MODE_BITS // fourth position, index = 3 +#define RC6_ADDRESS_BITS 8 +#define RC6_COMMAND_BITS 8 +#define RC6_CUSTOMER_BITS 14 + +#define RC6_BITS (RC6_LEADING_BIT + RC6_MODE_BITS + RC6_TOGGLE_BIT + RC6_ADDRESS_BITS + RC6_COMMAND_BITS) // 21 +#define RC6A_BITS (RC6_LEADING_BIT + RC6_MODE_BITS + RC6_TOGGLE_BIT + 1 + RC6_CUSTOMER_BITS + RC6_ADDRESS_BITS + RC6_COMMAND_BITS) // 36 + +#define RC6_UNIT 444 // 16 periods of 36 kHz (444.4444) + +#define RC6_HEADER_MARK (6 * RC6_UNIT) // 2666 +#define RC6_HEADER_SPACE (2 * RC6_UNIT) // 889 + +#define RC6_TRAILING_SPACE (6 * RC6_UNIT) // 2666 +#define MIN_RC6_MARKS 4 + ((RC6_ADDRESS_BITS + RC6_COMMAND_BITS) / 2) // 12, 4 are for preamble + +#define RC6_REPEAT_DISTANCE 107000 // just a guess but > 2.666ms +#define RC6_MAXIMUM_REPEAT_DISTANCE (RC6_REPEAT_DISTANCE + (RC6_REPEAT_DISTANCE / 4)) // Just a guess + +/** + * Main RC6 send function + */ +void IRsend::sendRC6(uint32_t aRawData, uint8_t aNumberOfBitsToSend) { + sendRC6Raw(aRawData, aNumberOfBitsToSend); +} +void IRsend::sendRC6Raw(uint32_t aRawData, uint8_t aNumberOfBitsToSend) { +// Set IR carrier frequency + enableIROut (RC5_RC6_KHZ); + +// Header + mark(RC6_HEADER_MARK); + space(RC6_HEADER_SPACE); + +// Start bit + mark(RC6_UNIT); + space(RC6_UNIT); + +// Data MSB first + uint32_t mask = 1UL << (aNumberOfBitsToSend - 1); + for (uint_fast8_t i = 1; mask; i++, mask >>= 1) { + // The fourth bit we send is the "double width toggle bit" + unsigned int t = (i == (RC6_TOGGLE_BIT_INDEX + 1)) ? (RC6_UNIT * 2) : (RC6_UNIT); + if (aRawData & mask) { + mark(t); + space(t); + } else { + space(t); + mark(t); + } + } +} + +/** + * Send RC6 64 bit raw data + * Can be used to send RC6A with ?31? data bits + */ +void IRsend::sendRC6(uint64_t aRawData, uint8_t aNumberOfBitsToSend) { + sendRC6Raw(aRawData, aNumberOfBitsToSend); +} +void IRsend::sendRC6Raw(uint64_t aRawData, uint8_t aNumberOfBitsToSend) { +// Set IR carrier frequency + enableIROut (RC5_RC6_KHZ); + +// Header + mark(RC6_HEADER_MARK); + space(RC6_HEADER_SPACE); + +// Start bit + mark(RC6_UNIT); + space(RC6_UNIT); + +// Data MSB first + uint64_t mask = 1ULL << (aNumberOfBitsToSend - 1); + for (uint_fast8_t i = 1; mask; i++, mask >>= 1) { + // The fourth bit we send is the "double width toggle bit" + unsigned int t = (i == (RC6_TOGGLE_BIT_INDEX + 1)) ? (RC6_UNIT * 2) : (RC6_UNIT); + if (aRawData & mask) { + mark(t); + space(t); + } else { + space(t); + mark(t); + } + } +} + +/** + * Assemble raw data for RC6 from parameters and toggle state and send + * We do not wait for the minimal trailing space of 2666 us + * @param aEnableAutomaticToggle Send toggle bit according to the state of the static sLastSendToggleValue variable. + */ +void IRsend::sendRC6(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle) { + + LongUnion tIRRawData; + tIRRawData.UByte.LowByte = aCommand; + tIRRawData.UByte.MidLowByte = aAddress; + + tIRRawData.UWord.HighWord = 0; // must clear high word + if (aEnableAutomaticToggle) { + if (sLastSendToggleValue == 0) { + sLastSendToggleValue = 1; + // set toggled bit + IR_DEBUG_PRINT(F("Set Toggle ")); + tIRRawData.UByte.MidHighByte = 1; // 3 Mode bits are 0 + } else { + sLastSendToggleValue = 0; + } + } + +#if defined(LOCAL_DEBUG) + Serial.print(F("RC6: ")); + Serial.print(F("sLastSendToggleValue=")); + Serial.print (sLastSendToggleValue); + Serial.print(F(" RawData=")); + Serial.println(tIRRawData.ULong, HEX); +#endif + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + + // start and leading bits are sent by sendRC6 + sendRC6Raw(tIRRawData.ULong, RC6_BITS - 1); // -1 since the leading bit is additionally sent by sendRC6 + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + // send repeated command in a fixed raster + delay(RC6_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI); + } + } +} + +/** + * Assemble raw data for RC6 from parameters and toggle state and send + * We do not wait for the minimal trailing space of 2666 us + * @param aEnableAutomaticToggle Send toggle bit according to the state of the static sLastSendToggleValue variable. + */ +void IRsend::sendRC6A(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint16_t aCustomer, + bool aEnableAutomaticToggle) { + + LongUnion tIRRawData; + tIRRawData.UByte.LowByte = aCommand; + tIRRawData.UByte.MidLowByte = aAddress; + + tIRRawData.UWord.HighWord = aCustomer | 0x400; // bit 31 is always 1 + + if (aEnableAutomaticToggle) { + if (sLastSendToggleValue == 0) { + sLastSendToggleValue = 1; + // set toggled bit + IR_DEBUG_PRINT(F("Set Toggle ")); + tIRRawData.UByte.HighByte |= 0x80; // toggle bit is bit 32 + } else { + sLastSendToggleValue = 0; + } + } + + // Set mode bits + uint64_t tRawData = tIRRawData.ULong + 0x0600000000; + +#if defined(LOCAL_DEBUG) + Serial.print(F("RC6A: ")); + Serial.print(F("sLastSendToggleValue=")); + Serial.print (sLastSendToggleValue); + Serial.print(F(" RawData=")); + Serial.println(tIRRawData.ULong, HEX); +#endif + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + + // start and leading bits are sent by sendRC6 + sendRC6Raw(tRawData, RC6A_BITS - 1); // -1 since the leading bit is additionally sent by sendRC6 + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + // send repeated command in a fixed raster + delay(RC6_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI); + } + } +} + +/** + * Try to decode data as RC6 protocol + */ +bool IRrecv::decodeRC6() { + uint8_t tBitIndex; + uint32_t tDecodedRawData = 0; + + // Check we have the right amount of data (). The +3 for initial gap, start bit mark and space + if (decodedIRData.rawlen < MIN_RC6_MARKS + 3 && (RC6_BITS + 3) < decodedIRData.rawlen) { + IR_DEBUG_PRINT(F("RC6: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not between 15 and 25")); + return false; + } + + // Check header "mark" and "space", this must be done for repeat and data + if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], RC6_HEADER_MARK) + || !matchSpace(decodedIRData.rawDataPtr->rawbuf[2], RC6_HEADER_SPACE)) { + // no debug output, since this check is mainly to determine the received protocol + IR_DEBUG_PRINT(F("RC6: ")); + IR_DEBUG_PRINTLN(F("Header mark or space length is wrong")); + return false; + } + + // Set Biphase decoding start values + initBiphaselevel(3, RC6_UNIT); // Skip gap-space and start-bit mark + space + +// Process first bit, which is known to be a 1 (mark->space) + if (getBiphaselevel() != MARK) { + IR_DEBUG_PRINT(F("RC6: ")); + IR_DEBUG_PRINTLN(F("first getBiphaselevel() is not MARK")); + return false; + } + if (getBiphaselevel() != SPACE) { + IR_DEBUG_PRINT(F("RC6: ")); + IR_DEBUG_PRINTLN(F("second getBiphaselevel() is not SPACE")); + return false; + } + + for (tBitIndex = 0; sBiphaseDecodeRawbuffOffset < decodedIRData.rawlen; tBitIndex++) { + uint8_t tStartLevel; // start level of coded bit + uint8_t tEndLevel; // end level of coded bit + + tStartLevel = getBiphaselevel(); + if (tBitIndex == RC6_TOGGLE_BIT_INDEX) { + // Toggle bit is double wide; make sure second half is equal first half + if (tStartLevel != getBiphaselevel()) { +#if defined(LOCAL_DEBUG) + Serial.print(F("RC6: ")); + Serial.println(F("Toggle mark or space length is wrong")); +#endif + return false; + } + } + + tEndLevel = getBiphaselevel(); + if (tBitIndex == RC6_TOGGLE_BIT_INDEX) { + // Toggle bit is double wide; make sure second half matches + if (tEndLevel != getBiphaselevel()) { +#if defined(LOCAL_DEBUG) + Serial.print(F("RC6: ")); + Serial.println(F("Toggle mark or space length is wrong")); +#endif + return false; + } + } + + /* + * Determine tDecodedRawData bit value by checking the transition type + */ + if ((tStartLevel == MARK) && (tEndLevel == SPACE)) { + // we have a mark to space transition here + tDecodedRawData = (tDecodedRawData << 1) | 1; // inverted compared to RC5 + } else if ((tStartLevel == SPACE) && (tEndLevel == MARK)) { + // we have a space to mark transition here + tDecodedRawData = (tDecodedRawData << 1) | 0; + } else { +#if defined(LOCAL_DEBUG) + Serial.print(F("RC6: ")); + Serial.println(F("Decode failed")); +#endif + // we have no transition here or one level is -1 -> error + return false; // Error + } + } + +// Success + decodedIRData.numberOfBits = tBitIndex; + + LongUnion tValue; + tValue.ULong = tDecodedRawData; + decodedIRData.decodedRawData = tDecodedRawData; + + if (tBitIndex < 35) { + // RC6 8 address bits, 8 command bits + decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; + decodedIRData.command = tValue.UByte.LowByte; + decodedIRData.address = tValue.UByte.MidLowByte; + // Check for toggle flag + if ((tValue.UByte.MidHighByte & 1) != 0) { + decodedIRData.flags = IRDATA_FLAGS_TOGGLE_BIT | IRDATA_FLAGS_IS_MSB_FIRST; + } + if (tBitIndex > 20) { + decodedIRData.flags |= IRDATA_FLAGS_EXTRA_INFO; + } + decodedIRData.protocol = RC6; + + } else { + // RC6A + decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST | IRDATA_FLAGS_EXTRA_INFO; + decodedIRData.command = tValue.UByte.LowByte; + decodedIRData.address = tValue.UByte.MidLowByte; + decodedIRData.extra = tValue.UWord.HighWord & 0x3FFF; // Mask to 14 bits, remove toggle and constant 1 + if ((tValue.UByte.HighByte & 0x80) != 0) { + decodedIRData.flags |= IRDATA_FLAGS_TOGGLE_BIT; + } + decodedIRData.protocol = RC6A; + } + + // check for repeat, do not check toggle bit yet + checkForRepeatSpaceTicksAndSetFlag(RC6_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK); + + return true; +} + +/********************************************************************************* + * Old deprecated functions, kept for backward compatibility to old 2.0 tutorials + *********************************************************************************/ + +/** + * Old version with 32 bit data + */ +void IRsend::sendRC5(uint32_t data, uint8_t nbits) { + // Set IR carrier frequency + enableIROut (RC5_RC6_KHZ); + + // Start + mark(RC5_UNIT); + space(RC5_UNIT); + mark(RC5_UNIT); + + // Data - Biphase code MSB first + for (uint32_t mask = 1UL << (nbits - 1); mask; mask >>= 1) { + if (data & mask) { + space(RC5_UNIT); // 1 is space, then mark + mark(RC5_UNIT); + } else { + mark(RC5_UNIT); + space(RC5_UNIT); + } + } +} + +/* + * Not longer required, use sendRC5(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle) instead + */ +void IRsend::sendRC5ext(uint8_t addr, uint8_t cmd, bool toggle) { +// Set IR carrier frequency + enableIROut (RC5_RC6_KHZ); + + uint8_t addressBits = 5; + uint8_t commandBits = 7; +// unsigned long nbits = addressBits + commandBits; + +// Start + mark(RC5_UNIT); + +// Bit #6 of the command part, but inverted! + uint8_t cmdBit6 = (1UL << (commandBits - 1)) & cmd; + if (cmdBit6) { + // Inverted (1 -> 0 = mark-to-space) + mark(RC5_UNIT); + space(RC5_UNIT); + } else { + space(RC5_UNIT); + mark(RC5_UNIT); + } + commandBits--; + +// Toggle bit + static int toggleBit = 1; + if (toggle) { + if (toggleBit == 0) { + toggleBit = 1; + } else { + toggleBit = 0; + } + } + if (toggleBit) { + space(RC5_UNIT); + mark(RC5_UNIT); + } else { + mark(RC5_UNIT); + space(RC5_UNIT); + } + +// Address + for (uint_fast8_t mask = 1UL << (addressBits - 1); mask; mask >>= 1) { + if (addr & mask) { + space(RC5_UNIT); // 1 is space, then mark + mark(RC5_UNIT); + } else { + mark(RC5_UNIT); + space(RC5_UNIT); + } + } + +// Command + for (uint_fast8_t mask = 1UL << (commandBits - 1); mask; mask >>= 1) { + if (cmd & mask) { + space(RC5_UNIT); // 1 is space, then mark + mark(RC5_UNIT); + } else { + mark(RC5_UNIT); + space(RC5_UNIT); + } + } +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_RC5_RC6_HPP diff --git a/src/ir_Samsung.cpp b/src/ir_Samsung.cpp deleted file mode 100644 index 4e7d164ce..000000000 --- a/src/ir_Samsung.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "IRremote.h" - -//============================================================================== -// SSSS AAA MMM SSSS U U N N GGGG -// S A A M M M S U U NN N G -// SSS AAAAA M M M SSS U U N N N G GG -// S A A M M S U U N NN G G -// SSSS A A M M SSSS UUU N N GGG -//============================================================================== - -#define SAMSUNG_BITS 32 -#define SAMSUNG_HEADER_MARK 4500 -#define SAMSUNG_HEADER_SPACE 4500 -#define SAMSUNG_BIT_MARK 560 -#define SAMSUNG_ONE_SPACE 1600 -#define SAMSUNG_ZERO_SPACE 560 -#define SAMSUNG_REPEAT_SPACE 2250 - -//+============================================================================= -#if SEND_SAMSUNG -void IRsend::sendSAMSUNG(unsigned long data, int nbits) { - // Set IR carrier frequency - enableIROut(38); - - // Header - mark(SAMSUNG_HEADER_MARK); - space(SAMSUNG_HEADER_SPACE); - - // Data - sendPulseDistanceWidthData(SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, data, nbits); - - // Footer - mark(SAMSUNG_BIT_MARK); - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -// SAMSUNGs have a repeat only 4 items long -// -#if DECODE_SAMSUNG -bool IRrecv::decodeSAMSUNG() { - int offset = 1; // Skip first space - - // Initial mark - if (!MATCH_MARK(results.rawbuf[offset], SAMSUNG_HEADER_MARK)) { - return false; - } - offset++; - -// Check for repeat - if ((irparams.rawlen == 4) && MATCH_SPACE(results.rawbuf[offset], SAMSUNG_REPEAT_SPACE) - && MATCH_MARK(results.rawbuf[offset + 1], SAMSUNG_BIT_MARK)) { - results.bits = 0; - results.value = REPEAT; - results.isRepeat = true; - results.decode_type = SAMSUNG; - return true; - } - if (irparams.rawlen < (2 * SAMSUNG_BITS) + 4) { - return false; - } - -// Initial space - if (!MATCH_SPACE(results.rawbuf[offset], SAMSUNG_HEADER_SPACE)) { - return false; - } - offset++; - - if (!decodePulseDistanceData(SAMSUNG_BITS, offset, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_ZERO_SPACE)) { - return false; - } - -// Success - results.bits = SAMSUNG_BITS; - results.decode_type = SAMSUNG; - return true; -} - -bool IRrecv::decodeSAMSUNG(decode_results *aResults) { - bool aReturnValue = decodeSAMSUNG(); - *aResults = results; - return aReturnValue; -} -#endif - diff --git a/src/ir_Samsung.hpp b/src/ir_Samsung.hpp new file mode 100644 index 000000000..976e88078 --- /dev/null +++ b/src/ir_Samsung.hpp @@ -0,0 +1,437 @@ +/* + * ir_Samsung.hpp + * + * Contains functions for receiving and sending Samsung IR Protocol in "raw" and standard format with 16 bit address and 16 or 32 bit command + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2017-2024 Darryl Smith, Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONSAMSUNGTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_SAMSUNG_HPP +#define _IR_SAMSUNG_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +//============================================================================== +// SSSS AAA MMM SSSS U U N N GGGG +// S A A M M M S U U NN N G +// SSS AAAAA M M M SSS U U N N N G GG +// S A A M M S U U N NN G G +// SSSS A A M M SSSS UUU N N GGG +//============================================================================== +/* + * Address=0xFFF1 Command=0x76 Raw-Data=0x8976FFF1 + +4500,-4400 + + 600,-1600 + 650,- 500 + 600,- 500 + 650,- 500 + + 600,-1650 + 600,-1600 + 650,-1600 + 600,-1650 + + 600,-1600 + 600,-1650 + 600,-1650 + 600,-1600 + + 600,-1650 + 600,-1650 + 600,-1600 + 600,-1650 + + 600,- 500 + 650,-1600 + 600,-1650 + 600,- 500 + + 650,-1600 + 600,-1650 + 600,-1650 + 600,- 500 + + 600,-1650 + 600,- 500 + 600,- 550 + 600,-1600 + + 600,- 550 + 600,- 550 + 550,- 550 + 600,-1650 + + 550 + Sum: 68750 + */ +/* + * Samsung repeat frame can be the original frame again or a special short repeat frame, + * then we call the protocol SamsungLG. They differ only in the handling of repeat, + * so we can not decide for the first frame which protocol is used. + */ +// see http://www.hifi-remote.com/wiki/index.php?title=DecodeIR#Samsung +// https://www.mikrocontroller.net/articles/IRMP_-_english#SAMSUNG32 +// https://www.mikrocontroller.net/articles/IRMP_-_english#SAMSUNG48 +// LSB first, 1 start bit + 16 bit address + 16 or 32 bit data + 1 stop bit. +// Here https://forum.arduino.cc/t/klimaanlage-per-ir-steuern/1051381/10 the address (0xB24D) is also 8 bits and then 8 inverted bits +// +// Here https://github.com/flipperdevices/flipperzero-firmware/blob/master/lib/infrared/encoder_decoder/samsung/infrared_decoder_samsung.c#L18 +// Address is 8 bit + same 8 bit if command is 8 bit and ~8 bit. +// +// So we assume 8 bit address, if command is 8 bit and ~8 bit! +// +// IRP notation: {38k,5553}<1,-1|1,-3>(8,-8,D:8,S:8,F:8,~F:8,1,^110)+ ==> 8 bit + 8 bit inverted data - Samsung32 +// IRP notation: {38k,5553}<1,-1|1,-3>(8,-8,D:8,S:8,F:16,1,^110)+ ==> 16 bit data - still Samsung32 +// IRP notation: {38k,5553}<1,-1|1,-3>(8,-8,D:8,S:8,F:8,~F:8,G:8,~G:8,1,^110)+ ==> 2 x (8 bit + 8 bit inverted data) - Samsung48 +// +#define SAMSUNG_ADDRESS_BITS 16 +#define SAMSUNG_COMMAND16_BITS 16 +#define SAMSUNG_COMMAND32_BITS 32 +#define SAMSUNG_BITS (SAMSUNG_ADDRESS_BITS + SAMSUNG_COMMAND16_BITS) +#define SAMSUNG48_BITS (SAMSUNG_ADDRESS_BITS + SAMSUNG_COMMAND32_BITS) + +// except SAMSUNG_HEADER_MARK, values are like NEC +#define SAMSUNG_UNIT 560 // 21.28 periods of 38 kHz, 11.2 ticks TICKS_LOW = 8.358 TICKS_HIGH = 15.0 +#define SAMSUNG_HEADER_MARK (8 * SAMSUNG_UNIT) // 4500 | 180 periods +#define SAMSUNG_HEADER_SPACE (8 * SAMSUNG_UNIT) // 4500 +#define SAMSUNG_BIT_MARK SAMSUNG_UNIT +#define SAMSUNG_ONE_SPACE (3 * SAMSUNG_UNIT) // 1690 | 33.8 TICKS_LOW = 25.07 TICKS_HIGH = 45.0 +#define SAMSUNG_ZERO_SPACE SAMSUNG_UNIT + +#define SAMSUNG_AVERAGE_DURATION 55000 // SAMSUNG_HEADER_MARK + SAMSUNG_HEADER_SPACE + 32 * 2,5 * SAMSUNG_UNIT + SAMSUNG_UNIT // 2.5 because we assume more zeros than ones +#define SAMSUNG_REPEAT_DURATION (SAMSUNG_HEADER_MARK + SAMSUNG_HEADER_SPACE + SAMSUNG_BIT_MARK + SAMSUNG_ZERO_SPACE + SAMSUNG_BIT_MARK) +#define SAMSUNG_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down. +#define SAMSUNG_MAXIMUM_REPEAT_DISTANCE (SAMSUNG_REPEAT_PERIOD + (SAMSUNG_REPEAT_PERIOD / 4)) // 137000 - Just a guess + +// 19 byte RAM +struct PulseDistanceWidthProtocolConstants const SamsungProtocolConstants PROGMEM = {SAMSUNG, SAMSUNG_KHZ, SAMSUNG_HEADER_MARK, + SAMSUNG_HEADER_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, + (SAMSUNG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr}; + +struct PulseDistanceWidthProtocolConstants const SamsungLGProtocolConstants PROGMEM = {SAMSUNGLG, SAMSUNG_KHZ, SAMSUNG_HEADER_MARK, + SAMSUNG_HEADER_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, + (SAMSUNG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), &sendSamsungLGSpecialRepeat}; +/************************************ + * Start of send and decode functions + ************************************/ + +/** + * Send repeat + * Repeat commands should be sent in a 110 ms raster. + * This repeat was sent by an LG 6711R1P071A remote + */ +void IRsend::sendSamsungLGRepeat() { + enableIROut (SAMSUNG_KHZ); // 38 kHz + mark(SAMSUNG_HEADER_MARK); // + 4500 + space(SAMSUNG_HEADER_SPACE); // - 4500 + mark(SAMSUNG_BIT_MARK); // + 560 + space(SAMSUNG_ZERO_SPACE); // - 560 + mark(SAMSUNG_BIT_MARK); // + 560 +} + +/** + * Like above, but implemented as a static function + * Used for sending special repeat frame. + * For use in ProtocolConstants. Saves up to 250 bytes compared to a member function. + */ +void sendSamsungLGSpecialRepeat() { + IrSender.enableIROut(SAMSUNG_KHZ); // 38 kHz + IrSender.mark(SAMSUNG_HEADER_MARK); // + 4500 + IrSender.space(SAMSUNG_HEADER_SPACE); // - 4500 + IrSender.mark(SAMSUNG_BIT_MARK); // + 560 + IrSender.space(SAMSUNG_ZERO_SPACE); // - 560 + IrSender.mark(SAMSUNG_BIT_MARK); // + 560 +} + +/* + * Sent e.g. by an LG 6711R1P071A remote + * @param aAddress 16 bit address. If < 0x100, i.e. only 8 bit, then a (standard) 16 bit address <8_bit_address><8_bit_address> is generated. + * @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent + */ +void IRsend::sendSamsungLG(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) { + if (aNumberOfRepeats < 0) { + sendSamsungLGRepeat(); + return; + } + + // send 16 bit address and 8 command bits and then 8 inverted command bits LSB first + LongUnion tRawData; + tRawData.UWord.LowWord = aAddress; + if (aAddress < 0x100) { // This disables the sending of an (non standard?) 16 bit address with upper byte = 0 like 0x0014. + tRawData.UByte.MidLowByte = aAddress; // here we have 8 bit address which must be duplicated + } + tRawData.UByte.MidHighByte = aCommand; + tRawData.UByte.HighByte = ~aCommand; + + sendPulseDistanceWidth_P(&SamsungLGProtocolConstants, tRawData.ULong, SAMSUNG_BITS, aNumberOfRepeats); +} + +/** + * Here we send Samsung32 + * If we get a command < 0x100, we send command and then ~command + * If we get an address < 0x100, we send 8 bit address and then the same 8 bit address again, this makes it flipper IRDB compatible + * !!! Be aware, that this is flexible, but makes it impossible to send e.g. 0x0042 as 16 bit value!!! + * To force send 16 bit address, use: sendSamsung16BitAddressAndCommand(). + * @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent + */ +void IRsend::sendSamsung(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) { + + LongUnion tSendValue; + if (aAddress < 0x100) { + // This makes it flipper IRDB compatible: + // https://github.com/flipperdevices/flipperzero-firmware/blob/master/lib/infrared/encoder_decoder/samsung/infrared_decoder_samsung.c#L18 + // Duplicate address byte, if address is only 8 bit + tSendValue.UBytes[1] = aAddress; + tSendValue.UBytes[0] = aAddress; + } else { + tSendValue.UWords[0] = aAddress; + } + + if (aCommand < 0x100) { + // Send 8 command bits and then 8 inverted command bits LSB first + tSendValue.UBytes[2] = aCommand; + tSendValue.UBytes[3] = ~aCommand; + + } else { + // Send 16 command bits + tSendValue.UWords[1] = aCommand; + } + + sendPulseDistanceWidth_P(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats); +} + +/** + * Maybe no one needs it in the wild... + * As above, but we are able to send e.g. 0x0042 as 16 bit address + * @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent + */ +void IRsend::sendSamsung16BitAddressAnd8BitCommand(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + + LongUnion tSendValue; + // send 16 bit address + tSendValue.UWords[0] = aAddress; + // Send 8 command bits and then 8 inverted command bits LSB first + tSendValue.UBytes[2] = aCommand; + tSendValue.UBytes[3] = ~aCommand; + + sendPulseDistanceWidth_P(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats); +} + +/** + * Maybe no one needs it in the wild... + * As above, but we are able to send e.g. 0x0042 as 16 bit address + * @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent + */ +void IRsend::sendSamsung16BitAddressAndCommand(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) { + + LongUnion tSendValue; + // send 16 bit address + tSendValue.UWords[0] = aAddress; + // Send 16 command bits + tSendValue.UWords[1] = aCommand; + + sendPulseDistanceWidth_P(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats); +} +/** + * Here we send Samsung48 + * We send 2 x (8 bit command and then ~command) + */ +void IRsend::sendSamsung48(uint16_t aAddress, uint32_t aCommand, int_fast8_t aNumberOfRepeats) { + + // send 16 bit address and 2 x ( 8 command bits and then 8 inverted command bits) LSB first +#if __INT_WIDTH__ < 32 + uint32_t tRawSamsungData[2]; // prepare 2 long for Samsung48 + + LongUnion tSendValue; + tSendValue.UWords[0] = aAddress; + tSendValue.UBytes[2] = aCommand; + tSendValue.UBytes[3] = ~aCommand; + uint8_t tUpper8BitsOfCommand = aCommand >> 8; + tRawSamsungData[1] = tUpper8BitsOfCommand | (~tUpper8BitsOfCommand) << 8; + tRawSamsungData[0] = tSendValue.ULong; + + sendPulseDistanceWidthFromArray_P(&SamsungProtocolConstants, &tRawSamsungData[0], SAMSUNG48_BITS, aNumberOfRepeats); +#else + LongLongUnion tSendValue; + tSendValue.UWords[0] = aAddress; + if (aCommand < 0x10000) { + tSendValue.UBytes[2] = aCommand; + tSendValue.UBytes[3] = ~aCommand; + uint8_t tUpper8BitsOfCommand = aCommand >> 8; + tSendValue.UBytes[4] = tUpper8BitsOfCommand; + tSendValue.UBytes[5] = ~tUpper8BitsOfCommand; + } else { + tSendValue.ULongLong = aAddress | aCommand << 16; + } + sendPulseDistanceWidth_P(&SamsungProtocolConstants, tSendValue.ULongLong, SAMSUNG48_BITS, aNumberOfRepeats); +#endif +} + +/* + * We cannot decode frames with 8 command bits and then 8 inverted command bits LSB and 16 bit address, + * because in this case we always assume 8 bit address. + * This is, because we did not see 8 bit command and real 16 bit address in the wild. + */ +bool IRrecv::decodeSamsung() { + + // Check we have enough data (68). The +4 is for initial gap, start bit mark and space + stop bit mark + if (decodedIRData.rawlen != ((2 * SAMSUNG_BITS) + 4) && decodedIRData.rawlen != ((2 * SAMSUNG48_BITS) + 4) + && (decodedIRData.rawlen != 6)) { + IR_DEBUG_PRINT(F("Samsung: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not 6 or 68 or 100")); + return false; + } + + if (!checkHeader_P(&SamsungProtocolConstants)) { + return false; + } + + // Check for SansungLG style repeat + if (decodedIRData.rawlen == 6) { + decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_PROTOCOL_WITH_DIFFERENT_REPEAT | IRDATA_FLAGS_IS_LSB_FIRST; + decodedIRData.address = lastDecodedAddress; + decodedIRData.command = lastDecodedCommand; + decodedIRData.protocol = SAMSUNGLG; + return true; + } + + /* + * Decode first 32 bits + */ + if (!decodePulseDistanceWidthData_P(&SamsungProtocolConstants, SAMSUNG_BITS, 3)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Samsung: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } + LongUnion tValue; + tValue.ULong = decodedIRData.decodedRawData; + decodedIRData.address = tValue.UWord.LowWord; + + if (decodedIRData.rawlen == (2 * SAMSUNG48_BITS) + 4) { + /* + * Samsung48 + */ + // decode additional 16 bit + if (!decodePulseDistanceWidthData_P(&SamsungProtocolConstants, (SAMSUNG_COMMAND32_BITS - SAMSUNG_COMMAND16_BITS), + 3 + (2 * SAMSUNG_BITS))) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Samsung: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } + + /* + * LSB data is already in tValue.UWord.HighWord! + * Put latest (MSB) bits in LowWord, LSB first would have them expect in HighWord so keep this in mind for decoding below + */ + tValue.UWord.LowWord = decodedIRData.decodedRawData; // We have only 16 bit in decodedRawData here +#if __INT_WIDTH__ >= 32 + // workaround until complete refactoring for 64 bit + decodedIRData.decodedRawData = (decodedIRData.decodedRawData << 32)| tValue.UWord.HighWord << 16 | decodedIRData.address; // store all 48 bits in decodedRawData +#endif + + /* + * Check parity of 32 bit command + */ + // receive 2 * (8 bits then 8 inverted bits) LSB first + if (tValue.UByte.MidHighByte != (uint8_t)(~tValue.UByte.HighByte) + && tValue.UByte.LowByte != (uint8_t)(~tValue.UByte.MidLowByte)) { + decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_LSB_FIRST; + } + + decodedIRData.command = tValue.UByte.LowByte << 8 | tValue.UByte.MidHighByte; // low and high word are swapped here, so fetch it this way + decodedIRData.numberOfBits = SAMSUNG48_BITS; + decodedIRData.protocol = SAMSUNG48; + + } else { + /* + * Samsung32 + */ + if (tValue.UByte.MidHighByte == (uint8_t)(~tValue.UByte.HighByte)) { + // 8 bit command protocol -> assume 8 bit address + decodedIRData.command = tValue.UByte.MidHighByte; // first 8 bit + } + + if (tValue.UByte.MidLowByte == tValue.UByte.LowByte) { + decodedIRData.address = tValue.UByte.LowByte; // assume LowByte == MidLowByte as seen for a LG HX906 A/V Receive E8172C2C + } else { + // 16 bit command protocol, address is filled above with the 16 bit value + decodedIRData.command = tValue.UWord.HighWord; // first 16 bit + } + + decodedIRData.numberOfBits = SAMSUNG_BITS; + decodedIRData.protocol = SAMSUNG; + } + + // check for repeat + checkForRepeatSpaceTicksAndSetFlag(SAMSUNG_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK); + + return true; +} + +// Old version with MSB first +bool IRrecv::decodeSAMSUNG(decode_results *aResults) { + unsigned int offset = 1; // Skip first space + + // Initial mark + if (!matchMark(aResults->rawbuf[offset], SAMSUNG_HEADER_MARK)) { + return false; + } + offset++; + + // Check for repeat -- like a NEC repeat + if ((aResults->rawlen == 4) && matchSpace(aResults->rawbuf[offset], 2250) + && matchMark(aResults->rawbuf[offset + 1], SAMSUNG_BIT_MARK)) { + aResults->bits = 0; + aResults->value = 0xFFFFFFFF; + decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT; + decodedIRData.protocol = SAMSUNG; + return true; + } + if (aResults->rawlen < (2 * SAMSUNG_BITS) + 4) { + return false; + } + + // Initial space + if (!matchSpace(aResults->rawbuf[offset], SAMSUNG_HEADER_SPACE)) { + return false; + } + offset++; + + if (!decodePulseDistanceWidthData(SAMSUNG_BITS, offset, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST)) { + return false; + } + + // Success + aResults->value = decodedIRData.decodedRawData; + aResults->bits = SAMSUNG_BITS; + aResults->decode_type = SAMSUNG; + decodedIRData.protocol = SAMSUNG; + return true; +} + +// Old version with MSB first +void IRsend::sendSamsungMSB(unsigned long data, int nbits) { + // Set IR carrier frequency + enableIROut (SAMSUNG_KHZ); + + // Header + mark(SAMSUNG_HEADER_MARK); + space(SAMSUNG_HEADER_SPACE); + + // Old version with MSB first Data + stop bit + sendPulseDistanceWidthData(SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, data, nbits, + PROTOCOL_IS_MSB_FIRST); +} +void IRsend::sendSAMSUNG(unsigned long data, int nbits) { + sendSamsungMSB(data, nbits); +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_SAMSUNG_HPP diff --git a/src/ir_Sanyo.cpp b/src/ir_Sanyo.cpp deleted file mode 100644 index b228885c5..000000000 --- a/src/ir_Sanyo.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "IRremote.h" - -//============================================================================== -// SSSS AAA N N Y Y OOO -// S A A NN N Y Y O O -// SSS AAAAA N N N Y O O -// S A A N NN Y O O -// SSSS A A N N Y OOO -//============================================================================== - -// I think this is a Sanyo decoder: Serial = SA 8650B -// Looks like Sony except for timings, 48 chars of data and time/space different - -#define SANYO_BITS 12 -#define SANYO_HEADER_MARK 3500 // seen range 3500 -#define SANYO_HEADER_SPACE 950 // seen 950 -#define SANYO_ONE_MARK 2400 // seen 2400 -#define SANYO_ZERO_MARK 700 // seen 700 -#define SANYO_RPT_LENGTH 45000 // Not used. Commands are repeated every 45ms(measured from start to start) for as long as the key on the remote control is held down. -#define SANYO_DOUBLE_SPACE_USECS 800 // usually see 713 - not using ticks as get number wrap around - -//+============================================================================= -#if DECODE_SANYO -bool IRrecv::decodeSanyo() { - long data = 0; - unsigned int offset = 0; // Don't skip first space, check its size - - if (results.rawlen < (2 * SANYO_BITS) + 2) { - return false; - } - -#if 0 - // Put this back in for debugging - note can't use #DEBUG as if Debug on we don't see the repeat cos of the delay - Serial.print("IR Gap: "); - Serial.println( results.rawbuf[offset] * MICROS_PER_TICK); - Serial.println( "test against:"); - Serial.println(SANYO_DOUBLE_SPACE_USECS); -#endif - -// Initial space - if (results.rawbuf[offset] < (SANYO_DOUBLE_SPACE_USECS / MICROS_PER_TICK)) { - //Serial.print("IR Gap found: "); - results.bits = 0; - results.value = REPEAT; - results.isRepeat = true; - results.decode_type = SANYO; - return true; - } - offset++; - - // Initial mark - if (!MATCH_MARK(results.rawbuf[offset], SANYO_HEADER_MARK)) { - return false; - } - offset++; - - // Skip Second Mark - if (!MATCH_MARK(results.rawbuf[offset], SANYO_HEADER_MARK)) { - return false; - } - offset++; - - while (offset + 1 < irparams.rawlen) { - if (!MATCH_SPACE(results.rawbuf[offset], SANYO_HEADER_SPACE)) { - break; - } - offset++; - - if (MATCH_MARK(results.rawbuf[offset], SANYO_ONE_MARK)) { - data = (data << 1) | 1; - } else if (MATCH_MARK(results.rawbuf[offset], SANYO_ZERO_MARK)) { - data = (data << 1) | 0; - } else { - return false; - } - offset++; - } - - // Success - results.bits = (offset - 1) / 2; - if (results.bits < 12) { - results.bits = 0; - return false; - } - - results.value = data; - results.decode_type = SANYO; - return true; -} -bool IRrecv::decodeSanyo(decode_results *aResults) { - bool aReturnValue = decodeSanyo(); - *aResults = results; - return aReturnValue; -} -#endif diff --git a/src/ir_Sharp.cpp b/src/ir_Sharp.cpp deleted file mode 100644 index 61e884dc3..000000000 --- a/src/ir_Sharp.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "IRremote.h" - -//============================================================================== -// SSSS H H AAA RRRR PPPP -// S H H A A R R P P -// SSS HHHHH AAAAA RRRR PPPP -// S H H A A R R P -// SSSS H H A A R R P -//============================================================================== - -// Sharp and DISH support by Todd Treece: http://unionbridge.org/design/ircommand -// -// The send function has the necessary repeat built in because of the need to -// invert the signal. -// -// Sharp protocol documentation: -// http://www.sbprojects.com/knowledge/ir/sharp.htm -// -// Here is the LIRC file I found that seems to match the remote codes from the -// oscilloscope: -// Sharp LCD TV: -// http://lirc.sourceforge.net/remotes/sharp/GA538WJSA - -#define SHARP_BITS 15 -#define SHARP_ONE_SPACE 1805 -//#define SHARP_ONE_SPACE 1850 - -#define SHARP_ADDR_BITS 5 -#define SHARP_DATA_BITS 8 -#define SHARP_BIT_MARK_SEND 250 -#define SHARP_BIT_MARK_RECV 150 - -#define SHARP_ZERO_SPACE 795 -#define SHARP_GAP 600000 // Not used. -#define SHARP_REPEAT_SPACE 3000 - -#define SHARP_TOGGLE_MASK 0x3FF - -//+============================================================================= -#if SEND_SHARP -void IRsend::sendSharpRaw(unsigned long data, int nbits) { - enableIROut(38); - - // Sending codes in bursts of 3 (normal, inverted, normal) makes transmission - // much more reliable. That's the exact behavior of CD-S6470 remote control. - for (int n = 0; n < 3; n++) { - sendPulseDistanceWidthData(SHARP_BIT_MARK_SEND, SHARP_ONE_SPACE, SHARP_BIT_MARK_SEND, SHARP_ZERO_SPACE, data, nbits); -// for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) { -// if (data & mask) { -// mark (SHARP_BIT_MARK_SEND); -// space(SHARP_ONE_SPACE); -// } else { -// mark (SHARP_BIT_MARK_SEND); -// space(SHARP_ZERO_SPACE); -// } -// } - - mark(SHARP_BIT_MARK_SEND); - space(SHARP_ZERO_SPACE); - delay(40); - - data = data ^ SHARP_TOGGLE_MASK; - } - - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -// Sharp send compatible with data obtained through decodeSharp() -// ^^^^^^^^^^^^^ FUNCTION MISSING! -// -#if SEND_SHARP -void IRsend::sendSharp(unsigned int address, unsigned int command) { - sendSharpRaw((address << 10) | (command << 2) | 2, SHARP_BITS); - /* - * Use this code instead of the line above to be code compatible to the decoded values from decodeSharp - */ -// //Change address to big-endian (five bits swap place) -// address = (address & 0x10) >> 4 | (address & 0x01) << 4 | (address & 0x08) >> 2 | (address & 0x02) << 2 | (address & 0x04) ; -// //Change command to big-endian (eight bit swap place) -// command = (command & 0xF0) >> 4 | (command & 0x0F) << 4; -// command = (command & 0xCC) >> 2 | (command & 0x33) << 2; -// command = (command & 0xAA) >> 1 | (command & 0x55) << 1; -// sendSharpRaw((address << 10) | (command << 2) | 0, SHARP_BITS); -} - -#endif // SEND_SHARP - -//+============================================================================= -// Sharp decode function written based on Sharp protocol documentation: -// http://www.sbprojects.com/knowledge/ir/sharp.htm -// Tesded on a DENON AVR-1804 reciever - -#if DECODE_SHARP -bool IRrecv::decodeSharp() { - unsigned long lastData = 0; // to store last data - int offset = 1; //skip long space - int loops = 1; //number of bursts - - // Check we have the right amount of data - // Either one burst or three where second is inverted - // The setting #define _GAP 5000 in IRremoteInt.h will give one burst and possibly three calls to this function - if (irparams.rawlen == (SHARP_BITS + 1) * 2) - loops = 1; - else if (irparams.rawlen == (SHARP_BITS + 1) * 2 * 3) - loops = 3; - else - return false; - - // Check the first mark to see if it fits the SHARP_BIT_MARK_RECV length - if (!MATCH_MARK(results.rawbuf[offset], SHARP_BIT_MARK_RECV)) - return false; - //check the first pause and see if it fits the SHARP_ONE_SPACE or SHARP_ZERO_SPACE length - if (!(MATCH_SPACE(results.rawbuf[offset + 1], SHARP_ONE_SPACE) || MATCH_SPACE(results.rawbuf[offset + 1], SHARP_ZERO_SPACE))) - return false; - - // Read the bits in - for (int j = 0; j < loops; j++) { - if (!decodePulseDistanceData(SHARP_ADDR_BITS, offset, SHARP_BIT_MARK_SEND, SHARP_ONE_SPACE, SHARP_ZERO_SPACE)) { - return false; - } - results.address = results.value; - - if (!decodePulseDistanceData( SHARP_DATA_BITS, offset + SHARP_ADDR_BITS, SHARP_BIT_MARK_SEND, SHARP_ONE_SPACE, - SHARP_ZERO_SPACE)) { - return false; - } - - //skip exp bit (mark+pause), chk bit (mark+pause), mark and long pause before next burst - offset += 6; - - //Check if last burst data is equal to this burst (lastData already inverted) - if (lastData != 0 && results.value != lastData) - return false; - //save current burst of data but invert (XOR) the last 10 bits (8 data bits + exp bit + chk bit) - lastData = results.value ^ 0xFF; - } - -// Success - results.bits = SHARP_BITS; - results.decode_type = SHARP; - return true; -} - -bool IRrecv::decodeSharp(decode_results *aResults) { - bool aReturnValue = decodeSharp(); - *aResults = results; - return aReturnValue; -} -#endif diff --git a/src/ir_Sharp_alt.cpp b/src/ir_Sharp_alt.cpp deleted file mode 100644 index de5e029a5..000000000 --- a/src/ir_Sharp_alt.cpp +++ /dev/null @@ -1,128 +0,0 @@ -//-*- mode: C; c-basic-offset: 8; tab-width: 8; indent-tabs-mode: t; -*- -#include "IRremote.h" - -//============================================================================== -// SSSS H H AAA RRRR PPPP AAA L TTTTT -// S H H A A R R P P A A L T -// SSS HHHHH AAAAA RRRR PPPP AAAAA L T -// S H H A A R R P A A L T -// SSSS H H A A R R P A A LLLLL T -//============================================================================== - -// This is an alternative protocol to that in ir_Sharp.cpp. It was tested with -// the original Sharp GA538WJSA remote control. LIRC file with codes for this -// remote control: http://lirc.sourceforge.net/remotes/sharp/GA538WJSA -// -// Author: Sergiy Kolesnikov -// - -#define SHARP_ALT_RAWLEN 32 -#define SHARP_ALT_ADDRESS_BITS 5 -#define SHARP_ALT_COMMAND_BITS 8 -#define SHARP_ALT_BIT_MARK 150 -#define SHARP_ALT_SEND_BIT_MARK 300 -#define SHARP_ALT_ONE_SPACE 1750 -#define SHARP_ALT_ZERO_SPACE 700 -#define SHARP_ALT_REPEAT_SPACE 50000 -#define SHARP_ALT_SEND_REPEAT_SPACE 44000 -#define SHARP_ALT_TOGGLE_MASK 0x3FF -#define SHARP_ALT_SEND_INVERT_MASK 0x7FE0 - -//+============================================================================= -#if SEND_SHARP_ALT -void IRsend::sendSharpAltRaw(unsigned int data, int nbits) { - enableIROut(38); - - for (int n = 0; n < 3; n++) { - // From LSB to MSB - sendPulseDistanceWidthData(SHARP_ALT_SEND_BIT_MARK, SHARP_ALT_ONE_SPACE, SHARP_ALT_SEND_BIT_MARK, SHARP_ALT_ZERO_SPACE, - data, nbits, false); -// unsigned long mask = 1UL; -// for (int i = 0; i < nbits; i++) { -// if (data & mask) { -// mark(SHARP_ALT_SEND_BIT_MARK); -// space(SHARP_ALT_ONE_SPACE); -// } else { -// mark(SHARP_ALT_SEND_BIT_MARK); -// space(SHARP_ALT_ZERO_SPACE); -// } -// mask <<= 1; -// } - mark(SHARP_ALT_BIT_MARK); - space(SHARP_ALT_SEND_REPEAT_SPACE); - data = data ^ SHARP_ALT_SEND_INVERT_MASK; - } -} - -void IRsend::sendSharpAlt(uint8_t address, uint8_t command) { - // 1 = The expansion and the check bits (01). - unsigned int data = (1 << SHARP_ALT_COMMAND_BITS) | command; - data = (data << SHARP_ALT_ADDRESS_BITS) | address; - - // (+2) is for the expansion and the check bits 0b01. - sendSharpAltRaw(data, SHARP_ALT_ADDRESS_BITS + SHARP_ALT_COMMAND_BITS + 2); -} -#endif - -//+============================================================================= -#if DECODE_SHARP_ALT -bool IRrecv::decodeSharpAlt() { - static boolean is_first_repeat = true; - - // Check we have enough data. - if (results.rawlen < (SHARP_ALT_RAWLEN)) - return false; - - // Check stop mark. - if (!MATCH_MARK(results.rawbuf[SHARP_ALT_RAWLEN - 1], SHARP_ALT_BIT_MARK)) - return false; - - // Check the "check bit." If this bit is not 0 than it is an inverted - // frame, which we ignore. - if (!MATCH_SPACE(results.rawbuf[SHARP_ALT_RAWLEN - 2], SHARP_ALT_ZERO_SPACE)) - return false; - - // Check for repeat. - long initial_space = ((long) results.rawbuf[0]) * MICROS_PER_TICK; - if (initial_space <= SHARP_ALT_REPEAT_SPACE) { - if (!is_first_repeat) { - results.bits = 0; - results.value = REPEAT; - results.isRepeat = true; - results.decode_type = SHARP; - return true; - } else { - // Ignore the first repeat that always comes after the - // inverted frame (even if the button was pressed only once). - is_first_repeat = false; - return false; - } - } - - // Decode bits. SHARP_ALT_RAWLEN-6 because index starts with 0 (-1) and we - // omit the timings for the stop mark (-1), the check bit (-2), and the - // expansion bit (-2). - uint16_t bits = 0; - for (uint8_t i = SHARP_ALT_RAWLEN - 6; i > 1; i -= 2) { - if (MATCH_SPACE(results.rawbuf[i], SHARP_ALT_ONE_SPACE)) { - bits = (bits << 1) | 1; - } else if (MATCH_SPACE(results.rawbuf[i], SHARP_ALT_ZERO_SPACE)) { - bits = (bits << 1) | 0; - } else { - return false; - } - } - - results.bits = SHARP_ALT_ADDRESS_BITS + SHARP_ALT_COMMAND_BITS; - results.address = (bits & (1 << (SHARP_ALT_ADDRESS_BITS))) - 1; - results.value = bits >> SHARP_ALT_ADDRESS_BITS; // command - results.decode_type = SHARP_ALT; - is_first_repeat = true; - return true; -} -bool IRrecv::decodeSharpAlt(decode_results *aResults) { - bool aReturnValue = decodeSharpAlt(); - *aResults = results; - return aReturnValue; -} -#endif diff --git a/src/ir_Sony.cpp b/src/ir_Sony.cpp deleted file mode 100644 index 7dd08be4b..000000000 --- a/src/ir_Sony.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "IRremote.h" - -//============================================================================== -// SSSS OOO N N Y Y -// S O O NN N Y Y -// SSS O O N N N Y -// S O O N NN Y -// SSSS OOO N N Y -//============================================================================== - -// see https://www.sbprojects.net/knowledge/ir/sirc.php -// pulse width protocol - -#define SONY_BITS 12 -#define SONY_HEADER_MARK 2400 -#define SONY_SPACE 600 -#define SONY_ONE_MARK 1200 -#define SONY_ZERO_MARK 600 -#define SONY_RPT_LENGTH 45000 // Not used. Commands are repeated every 45ms(measured from start to start) for as long as the key on the remote control is held down. -#define SONY_DOUBLE_SPACE_USECS 500 // usually see 713 - not using ticks as get number wrap around - -//+============================================================================= -#if SEND_SONY -void IRsend::sendSony(unsigned long data, int nbits) { - // Set IR carrier frequency - enableIROut(40); - - // Header - mark(SONY_HEADER_MARK); - space (SONY_SPACE); - - sendPulseDistanceWidthData(SONY_ONE_MARK, SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, data, nbits); - /* - * Pulse width coding, the short version. - * Use this if need to save program space and you only require this protocol. - */ -// for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) { -// if (data & mask) { -// mark(SONY_ONE_MARK); -// space(SONY_SPACE); -// } else { -// mark(SONY_ZERO_MARK); -// space(SONY_SPACE); -// } -// } - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -#if DECODE_SONY -bool IRrecv::decodeSony() { - long data = 0; - unsigned int offset = 0; // Dont skip first space, check its size - - if (results.rawlen < (2 * SONY_BITS) + 2) { - return false; - } - - // Some Sony's deliver repeats fast after first - // unfortunately can't spot difference from of repeat from two fast clicks - if (results.rawbuf[offset] < (SONY_DOUBLE_SPACE_USECS / MICROS_PER_TICK)) { - DBG_PRINTLN("IR Gap found"); - results.bits = 0; - results.value = REPEAT; - results.isRepeat = true; - results.decode_type = UNKNOWN; - return true; - } - offset++; - - // Check header "mark" - if (!MATCH_MARK(results.rawbuf[offset], SONY_HEADER_MARK)) { - return false; - } - offset++; - - // Check header "space" - if (!MATCH_SPACE(results.rawbuf[offset], SONY_SPACE)) { - return false; - } - offset++; - - // MSB first - Not compatible to standard, which says LSB first :-( - while (offset < results.rawlen) { - // bit value is determined by length of the mark - if (MATCH_MARK(results.rawbuf[offset], SONY_ONE_MARK)) { - data = (data << 1) | 1; - } else if (MATCH_MARK(results.rawbuf[offset], SONY_ZERO_MARK)) { - data = (data << 1) | 0; - } else { - return false; - } - offset++; - - // check for the constant space length - if (!MATCH_SPACE(results.rawbuf[offset], SONY_SPACE)) { - return false; - } - offset++; - } - - results.bits = SONY_BITS; - results.value = data; - results.decode_type = SONY; - return true; -} - -bool IRrecv::decodeSony(decode_results *aResults) { - bool aReturnValue = decodeSony(); - *aResults = results; - return aReturnValue; -} -#endif - diff --git a/src/ir_Sony.hpp b/src/ir_Sony.hpp new file mode 100644 index 000000000..bcb9eb9d2 --- /dev/null +++ b/src/ir_Sony.hpp @@ -0,0 +1,235 @@ +/* + * ir_Sony.hpp + * + * Contains functions for receiving and sending SIRCS/Sony IR Protocol in "raw" and standard format with 5 bit address 7 bit command + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_SONY_HPP +#define _IR_SONY_HPP + +#if defined(DEBUG) +#define LOCAL_DEBUG +#else +//#define LOCAL_DEBUG // This enables debug output only for this file +#endif + +/** \addtogroup Decoder Decoders and encoders for different protocols + * @{ + */ +//============================================================================== +// SSSS OOO N N Y Y +// S O O NN N Y Y +// SSS O O N N N Y +// S O O N NN Y +// SSSS OOO N N Y +//============================================================================== +/* + * Protocol=Sony Address=0x4B9 Command=0x7 Raw-Data=0x25C87 20 bits LSB first + +2550,- 400 + // 7 command bits + +1300,- 450 +1350,- 450 +1300,- 450 + 700,- 450 + + 700,- 450 + 750,- 450 + 700,- 400 + // (5,8,) 13 address bits and NO stop bit! + +1300,- 500 + + 700,- 450 + 700,- 450 +1300,- 500 +1300,- 450 + +1300,- 450 + 700,- 450 +1350,- 400 + 750,- 450 + + 700,- 450 +1300,- 450 + 700,- 450 + 700 + Sum: 31100 + */ +/* + * Sony is the only protocol using the pulse width encoding, which requires no stop bit + */ +// see https://www.sbprojects.net/knowledge/ir/sirc.php +// https://www.mikrocontroller.net/articles/IRMP_-_english#SIRCS +// Frames are repeated every 45ms (measured from start to start) for as long as the key on the remote control is held down. +// This leads to a 15 ms gap for a Sony20 protocol! +// Here http://picprojects.org.uk/projects/sirc/ it is claimed, that many Sony remotes send each frame a minimum of 3 times. But 1 repeat (2 sends) has also been seen in real life. +// LSB first, start bit + 7 command + 5 to 13 address, no stop bit +// IRP: Sony12 {40k,600}<1,-1|2,-1>(4,-1,F:7,D:5,^45m)+ ==> 40 kHz, Unit is 600, LSB, One mark is 2 units, Start bit is 4 units, 7 bit Function, 5 bit Device, no Stop bit, every 45 milliseconds +// IRP: Sony15 {40k,600}<1,-1|2,-1>(4,-1,F:7,D:8,^45m)+ ==> 8 bit Device +// IRP: Sony20 {40k,600}<1,-1|2,-1>(4,-1,F:7,D:5,S:8,^45m)+ ==> 5 bit Device, 8 bit Subdevice +// +#define SONY_ADDRESS_BITS 5 +#define SONY_COMMAND_BITS 7 +#define SONY_EXTRA_BITS 8 +#define SONY_BITS_MIN (SONY_COMMAND_BITS + SONY_ADDRESS_BITS) // 12 bits +#define SONY_BITS_15 (SONY_COMMAND_BITS + SONY_ADDRESS_BITS + 3) // 15 bits +#define SONY_BITS_MAX (SONY_COMMAND_BITS + SONY_ADDRESS_BITS + SONY_EXTRA_BITS) // 20 bits == SIRCS_20_PROTOCOL +#define SONY_UNIT 600 // 24 periods of 40kHz + +#define SONY_HEADER_MARK (4 * SONY_UNIT) // 2400 +#define SONY_ONE_MARK (2 * SONY_UNIT) // 1200 +#define SONY_ZERO_MARK SONY_UNIT +#define SONY_SPACE SONY_UNIT + +#define SONY_AVERAGE_DURATION_MIN 21000 // SONY_HEADER_MARK + SONY_SPACE + 12 * 2,5 * SONY_UNIT // 2.5 because we assume more zeros than ones +#define SONY_AVERAGE_DURATION_MAX 33000 // SONY_HEADER_MARK + SONY_SPACE + 20 * 2,5 * SONY_UNIT // 2.5 because we assume more zeros than ones +#define SONY_REPEAT_PERIOD 45000 // Commands are repeated every 45 ms (measured from start to start) for as long as the key on the remote control is held down. +#define SONY_MAXIMUM_REPEAT_DISTANCE (SONY_REPEAT_PERIOD - SONY_AVERAGE_DURATION_MIN) // 24 ms + +struct PulseDistanceWidthProtocolConstants const SonyProtocolConstants PROGMEM = { SONY, SONY_KHZ, SONY_HEADER_MARK, SONY_SPACE, SONY_ONE_MARK, +SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, PROTOCOL_IS_LSB_FIRST, (SONY_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr }; + +/************************************ + * Start of send and decode functions + ************************************/ + +/** + * @param numberOfBits should be one of SIRCS_12_PROTOCOL, SIRCS_15_PROTOCOL, SIRCS_20_PROTOCOL. Not checked! 20 -> send 13 address bits + */ +void IRsend::sendSony(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint8_t numberOfBits) { + uint32_t tData = (uint32_t) aAddress << 7 | (aCommand & 0x7F); + // send 5, 8, 13 address bits LSB first + sendPulseDistanceWidth_P(&SonyProtocolConstants, tData, numberOfBits, aNumberOfRepeats); +} + +bool IRrecv::decodeSony() { + + if (!checkHeader_P(&SonyProtocolConstants)) { + return false; + } + + // Check we have enough data. +2 for initial gap and start bit mark and space minus the last/MSB space. NO stop bit! 26, 32, 42 + if (decodedIRData.rawlen != (2 * SONY_BITS_MIN) + 2 && decodedIRData.rawlen != (2 * SONY_BITS_MAX) + 2 + && decodedIRData.rawlen != (2 * SONY_BITS_15) + 2) { + IR_DEBUG_PRINT(F("Sony: ")); + IR_DEBUG_PRINT(F("Data length=")); + IR_DEBUG_PRINT(decodedIRData.rawlen); + IR_DEBUG_PRINTLN(F(" is not 12, 15 or 20")); + return false; + } + + if (!decodePulseDistanceWidthData_P(&SonyProtocolConstants, (decodedIRData.rawlen - 1) / 2, 3)) { +#if defined(LOCAL_DEBUG) + Serial.print(F("Sony: ")); + Serial.println(F("Decode failed")); +#endif + return false; + } + + // Success +// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value + decodedIRData.command = decodedIRData.decodedRawData & 0x7F; // first 7 bits + decodedIRData.address = decodedIRData.decodedRawData >> 7; // next 5 or 8 or 13 bits + decodedIRData.numberOfBits = (decodedIRData.rawlen - 1) / 2; + decodedIRData.protocol = SONY; + + //Check for repeat + checkForRepeatSpaceTicksAndSetFlag(SONY_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK); + + return true; +} + +/********************************************************************************* + * Old deprecated functions, kept for backward compatibility to old 2.0 tutorials + *********************************************************************************/ + +/* + * Old version with MSB first data + */ +#define SONY_DOUBLE_SPACE_USECS 500 // usually see 713 - not using ticks as get number wrap around +bool IRrecv::decodeSonyMSB(decode_results *aResults) { + long data = 0; + uint8_t bits = 0; + unsigned int offset = 0; // Dont skip first space, check its size + + if (aResults->rawlen < (2 * SONY_BITS_MIN) + 2) { + return false; + } + + // Some Sony's deliver repeats fast after first + // unfortunately can't spot difference from of repeat from two fast clicks + if (aResults->rawbuf[0] < (SONY_DOUBLE_SPACE_USECS / MICROS_PER_TICK)) { +#if defined(LOCAL_DEBUG) + Serial.println(F("IR Gap found")); +#endif + aResults->bits = 0; + aResults->value = 0xFFFFFFFF; + decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT; + decodedIRData.protocol = SONY; + return true; + } + offset++; + + // Check header "mark" + if (!matchMark(aResults->rawbuf[offset], SONY_HEADER_MARK)) { + return false; + } + offset++; + + // MSB first - Not compatible to standard, which says LSB first :-( + while (offset + 1 < aResults->rawlen) { + + // First check for the constant space length, we do not have a space at the end of raw data + // we are lucky, since the start space is equal the data space. + if (!matchSpace(aResults->rawbuf[offset], SONY_SPACE)) { + return false; + } + offset++; + + // bit value is determined by length of the mark + if (matchMark(aResults->rawbuf[offset], SONY_ONE_MARK)) { + data = (data << 1) | 1; + } else if (matchMark(aResults->rawbuf[offset], SONY_ZERO_MARK)) { + data = (data << 1) | 0; + } else { + return false; + } + offset++; + bits++; + + } + + aResults->bits = bits; + aResults->value = data; + aResults->decode_type = SONY; + decodedIRData.protocol = SONY; + return true; +} + +/** + * Old version with MSB first data + */ +void IRsend::sendSonyMSB(unsigned long data, int nbits) { + // Set IR carrier frequency + enableIROut (SONY_KHZ); + + // Header + mark(SONY_HEADER_MARK); + space(SONY_SPACE); + + // Old version with MSB first Data + sendPulseDistanceWidthData(SONY_ONE_MARK, SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, data, nbits, PROTOCOL_IS_MSB_FIRST); +} +void IRsend::sendSony(unsigned long data, int nbits) { + sendSonyMSB(data, nbits); +} + +/** @}*/ +#if defined(LOCAL_DEBUG) +#undef LOCAL_DEBUG +#endif +#endif // _IR_SONY_HPP diff --git a/src/ir_Template.cpp b/src/ir_Template.cpp deleted file mode 100644 index 9615fb4ad..000000000 --- a/src/ir_Template.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* - Assuming the protocol we are adding is for the (imaginary) manufacturer: Shuzu - - Our fantasy protocol is a standard protocol, so we can use this standard - template without too much work. Some protocols are quite unique and will require - considerably more work in this file! It is way beyond the scope of this text to - explain how to reverse engineer "unusual" IR protocols. But, unless you own an - oscilloscope, the starting point is probably to use the rawDump.ino sketch and - try to spot the pattern! - - Before you start, make sure the IR library is working OK: - # Open up the Arduino IDE - # Load up the rawDump.ino example sketch - # Run it - - Now we can start to add our new protocol... - - 1. Copy this file to : ir_Shuzu.cpp - - 2. Replace all occurrences of "Shuzu" with the name of your protocol. - - 3. Tweak the #defines to suit your protocol. - - 4. If you're lucky, tweaking the #defines will make the default send() function - work. - - 5. Again, if you're lucky, tweaking the #defines will have made the default - decode() function work. - - You have written the code to support your new protocol! - - Now you must do a few things to add it to the IRremote system: - - 1. Open IRremote.h and make the following changes: - REMEMEBER to change occurences of "SHUZU" with the name of your protocol - - A. At the top, in the section "Supported Protocols", add: - #define DECODE_SHUZU 1 - #define SEND_SHUZU 1 - - B. In the section "enumerated list of all supported formats", add: - SHUZU, - to the end of the list (notice there is a comma after the protocol name) - - C. Further down in "Main class for receiving IR", add: - //...................................................................... - #if DECODE_SHUZU - bool decodeShuzu (decode_results *aResults) ; - #endif - - D. Further down in "Main class for sending IR", add: - //...................................................................... - #if SEND_SHUZU - void sendShuzu (unsigned long data, int nbits) ; - #endif - - E. Save your changes and close the file - - 2. Now open irRecv.cpp and make the following change: - - A. In the function IRrecv::decode(), add: - #ifdef DECODE_NEC - DBG_PRINTLN("Attempting Shuzu decode"); - if (decodeShuzu(results)) return true ; - #endif - - B. Save your changes and close the file - - You will probably want to add your new protocol to the example sketch - - 3. Open MyDocuments\Arduino\libraries\IRremote\examples\IRrecvDumpV2.ino - - A. In the encoding() function, add: - case SHUZU: Serial.print("SHUZU"); break ; - - Now open the Arduino IDE, load up the rawDump.ino sketch, and run it. - Hopefully it will compile and upload. - If it doesn't, you've done something wrong. Check your work. - If you can't get it to work - seek help from somewhere. - - If you get this far, I will assume you have successfully added your new protocol - There is one last thing to do. - - 1. Delete this giant instructional comment. - - 2. Send a copy of your work to us so we can include it in the library and - others may benefit from your hard work and maybe even write a song about how - great you are for helping them! :) - - Regards, - BlueChip - */ - -#include "IRremote.h" - -//============================================================================== -// -// -// S H U Z U -// -// -//============================================================================== - -#define SHUZU_BITS 32 // The number of bits in the command - -#define SHUZU_HEADER_MARK 1000 // The length of the Header:Mark -#define SHUZU_HEADER_SPACE 2000 // The lenght of the Header:Space - -#define SHUZU_BIT_MARK 3000 // The length of a Bit:Mark -#define SHUZU_ONE_SPACE 4000 // The length of a Bit:Space for 1's -#define SHUZU_ZERO_SPACE 5000 // The length of a Bit:Space for 0's - -#define SHUZU_OTHER 1234 // Other things you may need to define - -//+============================================================================= -// -#if SEND_SHUZU -void IRsend::sendShuzu(unsigned long data, int nbits) { - // Set IR carrier frequency - enableIROut(38); - - // Header - mark(SHUZU_HEADER_MARK); - space(SHUZU_HEADER_SPACE); - - // Data - sendPulseDistanceData(data, nbits, SHUZU_BIT_MARK, SHUZU_ONE_SPACE,SHUZU_BIT_MARK, SHUZU_ZERO_SPACE); -// for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) { -// if (data & mask) { -// mark(SHUZU_BIT_MARK); -// space(SHUZU_ONE_SPACE); -// } else { -// mark(SHUZU_BIT_MARK); -// space(SHUZU_ZERO_SPACE); -// } -// } - - // Footer - mark(SHUZU_BIT_MARK); - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -// -#if DECODE_SHUZU -bool IRrecv::decodeShuzu() { - unsigned long data = 0; // Somewhere to build our code - int offset = 1; // Skip the gap reading - - // Check we have the right amount of data - if (results.rawlen != 1 + 2 + (2 * SHUZU_BITS) + 1) { - return false; - } - - // Check initial Mark+Space match - if (!MATCH_MARK(results.rawbuf[offset], SHUZU_HEADER_MARK)) { - return false; - } - offset++; - - if (!MATCH_SPACE(results.rawbuf[offset], SHUZU_HEADER_SPACE)) { - return false; - } - offset++; - - data = decodePulseDistanceData(results, SHUZU_BITS, offset, SHUZU_BIT_MARK, SHUZU_ONE_SPACE, SHUZU_ZERO_SPACE); -// // Read the bits in -// for (int i = 0; i < SHUZU_BITS; i++) { -// // Each bit looks like: MARK + SPACE_1 -> 1 -// // or : MARK + SPACE_0 -> 0 -// if (!MATCH_MARK(results.rawbuf[offset], SHUZU_BIT_MARK)) { -// return false; -// } -// offset++; -// -// // IR data is big-endian, so we shuffle it in from the right: -// if (MATCH_SPACE(results.rawbuf[offset], SHUZU_ONE_SPACE)) { -// data = (data << 1) | 1; -// } else if (MATCH_SPACE(results.rawbuf[offset], SHUZU_ZERO_SPACE)) { -// data = (data << 1) | 0; -// } else { -// return false; -// } -// offset++; -// } - - // Success - results.bits = SHUZU_BITS; - results.value = data; - results.decode_type = SHUZU; - return true; -} -bool IRrecv::decodeShuzu(decode_results *aResults) { - bool aReturnValue = decodeShuzu(); -} -#endif diff --git a/src/ir_Template.hpp b/src/ir_Template.hpp new file mode 100644 index 000000000..ce1dab069 --- /dev/null +++ b/src/ir_Template.hpp @@ -0,0 +1,175 @@ +/* + Assuming the protocol we are adding is for the (imaginary) manufacturer: Shuzu + + Our fantasy protocol is a standard protocol, so we can use this standard + template without too much work. Some protocols are quite unique and will require + considerably more work in this file! It is way beyond the scope of this text to + explain how to reverse engineer "unusual" IR protocols. But, unless you own an + oscilloscope, the starting point is probably to use the ReceiveDump.ino sketch and + try to spot the pattern! + + Before you start, make sure the IR library is working OK: + # Open up the Arduino IDE + # Load up the ReceiveDump.ino example sketch + # Run it + # Analyze your data to have an idea, what is the header timing, the bit timing, the address, the command and the checksum of your protocol. + + Now we can start to add our new protocol... + + 1. Copy this file to : ir_.hpp + + 2. Replace all occurrences of "SHUZU" with the name of your protocol. + + 3. Tweak the #defines to suit your protocol. + + 4. If you're lucky, tweaking the #defines will make the decode and send() function + work. + + You have now written the code to support your new protocol! + + To integrate it into the IRremote library, you must search for "BOSEWAVE" + and add your protocol in the same way as it is already done for BOSEWAVE. + + You have to change the following files: + IRSend.hpp IRsend::write(IRData *aIRSendData + int_fast8_t aNumberOfRepeats) + IRProtocol.h Add it to decode_type_t + IRReceive.hpp IRrecv::decode() + printActiveIRProtocols(Print *aSerial) + getProtocolString(decode_type_t aProtocol) + IRremote.hpp At 3 occurrences of DECODE_XXX + IRremoteInt.h Add the declaration of the decode and send function + + Now open the Arduino IDE, load up the ReceiveDump.ino sketch, and run it. + Hopefully it will compile and upload. + If it doesn't, you've done something wrong. Check your work and look carefully at the error messages. + + If you get this far, I will assume you have successfully added your new protocol + + At last, delete this giant instructional comment. + + If you want us to include your work in the library so others may benefit + from your hard work, you have to extend the examples + IRremoteInfo, SmallReceiver, simple Receiver, SendDemo and UnitTest too + as well as the Readme.md + It is not an act, but required for completeness. + + Thanks + The maintainer + */ + +/* + * ir_Shuzu.hpp + * + * Contains functions for receiving and sending Shuzu IR Protocol ... + * + * Copyright (C) 2022 Shuzu Guru + * shuzu.guru@gmail.com + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************ + * MIT License + * + * Copyright (c) 2022 Unknown Contributor :-) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_SHUZU_HPP +#define _IR_SHUZU_HPP + +//============================================================================== +// +// +// S H U Z U +// +// +//============================================================================== +// see: https://www.... + +// LSB first, 1 start bit + 16 bit address + 8 bit command + 1 stop bit. +#define SHUZU_ADDRESS_BITS 16 // 16 bit address +#define SHUZU_COMMAND_BITS 8 // Command + +#define SHUZU_BITS (SHUZU_ADDRESS_BITS + SHUZU_COMMAND_BITS) // The number of bits in the protocol +#define SHUZU_UNIT 560 // All timings are in microseconds + +#define SHUZU_HEADER_MARK (16 * SHUZU_UNIT) // The length of the Header:Mark +#define SHUZU_HEADER_SPACE (8 * SHUZU_UNIT) // The length of the Header:Space + +#define SHUZU_BIT_MARK SHUZU_UNIT // The length of a Bit:Mark +#define SHUZU_ONE_SPACE (3 * SHUZU_UNIT) // The length of a Bit:Space for 1's +#define SHUZU_ZERO_SPACE SHUZU_UNIT // The length of a Bit:Space for 0's + +#define SHUZU_REPEAT_HEADER_SPACE (4 * SHUZU_UNIT) // 2250 + +#define SHUZU_REPEAT_PERIOD 110000 // From start to start +#define SHUZU_REPEAT_SPACE 45000 // SHUZU_REPEAT_PERIOD - default frame duration. Used for repeat detection. + +#define SHUZU_OTHER 1234 // Other things you may need to define + +// use BOSEWAVE, we have no SHUZU code +struct PulseDistanceWidthProtocolConstants const ShuzuProtocolConstants PROGMEM = {BOSEWAVE, 38, SHUZU_HEADER_MARK, SHUZU_HEADER_SPACE, + SHUZU_BIT_MARK, SHUZU_ONE_SPACE, SHUZU_BIT_MARK, SHUZU_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (SHUZU_REPEAT_PERIOD + / MICROS_IN_ONE_MILLI), nullptr}; + +/************************************ + * Start of send and decode functions + ************************************/ + +void IRsend::sendShuzu(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) { + + sendPulseDistanceWidth_P(&ShuzuProtocolConstants, (uint32_t) aCommand << 8 | aCommand, SHUZU_BITS, aNumberOfRepeats); +} + +bool IRrecv::decodeShuzu() { + /* + * First check for right data length + * Next check start bit / header + * Next try the decode + */ + // Check we have the right amount of data (28). The +4 is for initial gap, start bit mark and space + stop bit mark + if (decodedIRData.rawDataPtr->rawlen != (2 * SHUZU_BITS) + 4) { + // no debug output, since this check is mainly to determine the received protocol + return false; + } + + // Check header + if (!checkHeader_P(&ShuzuProtocolConstants)) { + return false; + } + + // Decode + if (!decodePulseDistanceData_P(&ShuzuProtocolConstants, SHUZU_BITS)) { + IR_DEBUG_PRINT(F("Shuzu: ")); + IR_DEBUG_PRINTLN(F("Decode failed")); + return false; + } + + // Success, interpret raw data +// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value + decodedIRData.command = decodedIRData.decodedRawData >> SHUZU_ADDRESS_BITS; // upper 8 bits of LSB first value + decodedIRData.address = decodedIRData.decodedRawData & 0xFFFF; // lowest 16 bit of LSB first value + decodedIRData.numberOfBits = SHUZU_BITS; + decodedIRData.protocol = BOSEWAVE; // we have no SHUZU code + + //Check for repeat + checkForRepeatSpaceAndSetFlag(SHUZU_REPEAT_SPACE / MICROS_IN_ONE_MILLI); + + return true; +} +#endif // _IR_SHUZU_HPP diff --git a/src/ir_Whynter.cpp b/src/ir_Whynter.cpp deleted file mode 100644 index d2b5b35e1..000000000 --- a/src/ir_Whynter.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "IRremote.h" - -//============================================================================== -// W W H H Y Y N N TTTTT EEEEE RRRRR -// W W H H Y Y NN N T E R R -// W W W HHHHH Y N N N T EEE RRRR -// W W W H H Y N NN T E R R -// WWW H H Y N N T EEEEE R R -//============================================================================== - -#define WHYNTER_BITS 32 -#define WHYNTER_HEADER_MARK 2850 -#define WHYNTER_HEADER_SPACE 2850 -#define WHYNTER_BIT_MARK 750 -#define WHYNTER_ONE_SPACE 2150 -#define WHYNTER_ZERO_SPACE 750 - -//+============================================================================= -#if SEND_WHYNTER -void IRsend::sendWhynter(unsigned long data, int nbits) { - // Set IR carrier frequency - enableIROut(38); - - // Start - mark(WHYNTER_BIT_MARK); - space(WHYNTER_ZERO_SPACE); - - // Header - mark(WHYNTER_HEADER_MARK); - space(WHYNTER_HEADER_SPACE); - - // Data - sendPulseDistanceWidthData(WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, WHYNTER_BIT_MARK, WHYNTER_ZERO_SPACE, data, nbits); -// for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) { -// if (data & mask) { -// mark(WHYNTER_ONE_MARK); -// space(WHYNTER_ONE_SPACE); -// } else { -// mark(WHYNTER_ZERO_MARK); -// space(WHYNTER_ZERO_SPACE); -// } -// } - -// Footer - mark(WHYNTER_BIT_MARK); - space(0); // Always end with the LED off -} -#endif - -//+============================================================================= -#if DECODE_WHYNTER -bool IRrecv::decodeWhynter() { - int offset = 1; // skip initial space - - // Check we have the right amount of data +5 for (start bit + header) mark and space + stop bit mark - if (results.rawlen <= (2 * WHYNTER_BITS) + 5) { - return false; - } - - // Sequence begins with a bit mark and a zero space - if (!MATCH_MARK(results.rawbuf[offset], WHYNTER_BIT_MARK)) { - return false; - } - offset++; - - if (!MATCH_SPACE(results.rawbuf[offset], WHYNTER_ZERO_SPACE)) { - return false; - } - offset++; - - // header mark and space - if (!MATCH_MARK(results.rawbuf[offset], WHYNTER_HEADER_MARK)) { - return false; - } - offset++; - - if (!MATCH_SPACE(results.rawbuf[offset], WHYNTER_HEADER_SPACE)) { - return false; - } - offset++; - - if (!decodePulseDistanceData(WHYNTER_BITS, offset, WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, WHYNTER_ZERO_SPACE)) { - return false; - } - - // trailing mark / stop bit - if (!MATCH_MARK(results.rawbuf[offset + (2 * WHYNTER_BITS)], WHYNTER_BIT_MARK)) { - DBG_PRINT("Stop bit verify failed"); - return false; - } - - // Success - results.bits = WHYNTER_BITS; - results.decode_type = WHYNTER; - return true; -} - -bool IRrecv::decodeWhynter(decode_results *aResults) { - bool aReturnValue = decodeWhynter(); - *aResults = results; - return aReturnValue; -} -#endif - diff --git a/src/nRF5.cpp b/src/nRF5.cpp deleted file mode 100644 index 7e3d05e3b..000000000 --- a/src/nRF5.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#if defined(NRF5) || defined(ARDUINO_ARCH_NRF52840) - -// This file contains functions specific to the nRF5. -// It uses Timer2 so you cannot use the Adafruit_Microbit display driver - -#include "IRremote.h" - -// "Idiot check" -#ifdef USE_DEFAULT_ENABLE_IR_IN -#error Must undef USE_DEFAULT_ENABLE_IR_IN -#endif - -void IRTimer(); // defined in IRremote.cpp, masqueraded as ISR(TIMER_INTR_NAME) - -//+============================================================================= -// initialization -// -void IRrecv::enableIRIn() { -// Interrupt Service Routine - Fires every 50uS - NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer; // Set the timer in Timer Mode - NRF_TIMER2->TASKS_CLEAR = 1; // clear the task first to be usable for later - NRF_TIMER2->PRESCALER = 4; // f TIMER = 16 MHz / (2 ^ PRESCALER ) : 4 -> 1 MHz, 1 uS - NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_16Bit; //Set counter to 16 bit resolution - NRF_TIMER2->CC[0] = 50; //Set value for TIMER2 compare register 0, to trigger every 50 uS - NRF_TIMER2->CC[1] = 0; //Set value for TIMER2 compare register 1 - - // Enable interrupt on Timer 2, for CC[0] compare match events - NRF_TIMER2->INTENSET = (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos); - NVIC_EnableIRQ (TIMER2_IRQn); - - NRF_TIMER2->TASKS_START = 1; // Start TIMER2 - - // timerAttachInterrupt(timer, &IRTimer, 1); - - // Initialize state machine variables - irparams.rcvstate = IR_REC_STATE_IDLE; - irparams.rawlen = 0; - - // Set pin modes - pinMode(irparams.recvpin, INPUT); -} - -void timer_pal(void) { - if ((NRF_TIMER2->EVENTS_COMPARE[0] != 0) && ((NRF_TIMER2->INTENSET & TIMER_INTENSET_COMPARE0_Msk) != 0)) { - NRF_TIMER2->EVENTS_COMPARE[0] = 0; //Clear compare register 0 event - IRTimer(); // call the IR-receive function - NRF_TIMER2->CC[0] += 50; - } -} - -/** TIMTER2 peripheral interrupt handler. This interrupt handler is called whenever there it a TIMER2 interrupt - * Don't mess with this line. really. - */ -extern "C" { -void TIMER2_IRQHandler(void) { - timer_pal(); -} -} - -#endif // NRF5 diff --git a/src/private/IRTimer.hpp b/src/private/IRTimer.hpp new file mode 100644 index 000000000..872c4eddf --- /dev/null +++ b/src/private/IRTimer.hpp @@ -0,0 +1,2064 @@ +/** + * @file IRTimer.hpp + * + * @brief All timer specific definitions are contained in this file. + * Sets IR_SEND_PIN if required, e.g. if SEND_PWM_BY_TIMER for AVR is defined, which restricts the output to a dedicated pin number + * + * timerConfigForSend(aFrequencyKHz) must set output pin mode and disable receive interrupt if it uses the same resource + * + * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * + ************************************************************************************* + * MIT License + * + * Copyright (c) 2021-2023 Armin Joachimsmeyer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ************************************************************************************ + */ +#ifndef _IR_TIMER_HPP +#define _IR_TIMER_HPP + +/** \addtogroup HardwareDependencies CPU / board dependent definitions + * @{ + */ +/** \addtogroup Timer Usage of timers for the different CPU / boards + * @{ + */ +/* + * Functions declared here + */ +void timerConfigForReceive(); // Initialization of 50 us timer, interrupts are still disabled +void timerEnableReceiveInterrupt(); // Enable interrupts of an initialized timer +void timerDisableReceiveInterrupt(); // Disable interrupts of an initialized timer +void timerResetInterruptPending(); // ISR helper function for some architectures, which require a manual reset +// of the pending interrupt (TIMER_REQUIRES_RESET_INTR_PENDING is defined). Otherwise empty. + +void timerConfigForSend(uint16_t aFrequencyKHz); // Initialization of timer hardware generated PWM, if defined(SEND_PWM_BY_TIMER) +void enableSendPWMByTimer(); // Switch on PWM generation +void disableSendPWMByTimer(); // Switch off PWM generation + +// SEND_PWM_BY_TIMER for different architectures is enabled / defined at IRremote.hpp line 195. +#if defined(SEND_PWM_BY_TIMER) && ( (defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE)) || defined(ARDUINO_ARCH_MBED) ) +#define SEND_PWM_DOES_NOT_USE_RECEIVE_TIMER // Receive timer and send generation timer are independent here. +#endif + +#if defined(IR_SEND_PIN) && defined(SEND_PWM_BY_TIMER) && !defined(SEND_PWM_DOES_NOT_USE_RECEIVE_TIMER) // For ESP32 etc. IR_SEND_PIN definition is useful +#undef IR_SEND_PIN // To avoid "warning: "IR_SEND_PIN" redefined". The user warning is done at IRremote.hpp line 202. +#endif + +// Macros for enabling timers for development +//#define SEND_PWM_BY_TIMER +//#define IR_USE_AVR_TIMER1 +//#define IR_USE_AVR_TIMER2 +//#define IR_USE_AVR_TIMER3 +//#define IR_USE_AVR_TIMER4 +//#define IR_USE_AVR_TIMER4_HS +//#define IR_USE_AVR_TIMER5 +//#define IR_USE_AVR_TIMER_TINY0 +//#define IR_USE_AVR_TIMER_TINY1 +//#define IR_USE_AVR_TIMER_A +//#define IR_USE_AVR_TIMER_B +//#define IR_USE_AVR_TIMER_D +//#define __MK20DX128__ +//#define __MKL26Z64__ +//#define __IMXRT1062__ +//#define ESP8266 +//#define ESP32 +//#define ARDUINO_ARCH_SAMD +//#define ARDUINO_ARCH_MBED +//#define ARDUINO_ARCH_RP2040 +//#define NRF5 +//#define __STM32F1__ +//#define STM32F1xx +//#define PARTICLE +//#define ARDUINO_ARCH_RENESAS + +#if defined (DOXYGEN) +/** + * Hardware / timer dependent pin number for sending IR if SEND_PWM_BY_TIMER is defined. Otherwise used as default for IrSender.sendPin. + */ +#define IR_SEND_PIN + +/** + * Configures the timer to be able to generate the receive sample interrupt, + * which consumes a small amount of CPU every 50 (MICROS_PER_TICK) us. + * The actual interrupt generation is controlled by timerEnableReceiveInterrupt() and timerDisableReceiveInterrupt(). + * timerConfigForReceive() is used exclusively by IRrecv::start() + */ +void timerConfigForReceive() { +} +/** + * Enables the receive sample timer interrupt, which consumes a small amount of CPU every 50 us. + */ +void timerEnableReceiveInterrupt() { +} + +/** + * Disables the receive sample timer interrupt. This must be done before using the timer for e.g. tone(). + * Is a synonym for calling end() or stop(). + */ +void timerDisableReceiveInterrupt() { +} + +/** + * IF PWM should be generated not by software, but by a timer, this function sets output pin mode, + * configures the timer for generating a PWM with duty cycle of IR_SEND_DUTY_CYCLE_PERCENT + * and disables the receive interrupt if it uses the same resource. + * For most architectures, the pin number(s) which can be used for output is determined by the timer used! + * The output of the PWM signal is controlled by enableSendPWMByTimer() and disableSendPWMByTimer(). + * timerConfigForSend() is used exclusively by IRsend::enableIROut(). + * @param aFrequencyKHz Frequency of the sent PWM signal in kHz. There is no practical reason to have a sub kHz resolution for sending frequency :-). + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { +} + +/** + * Enables output of the PWM signal of the timer at the timer pin. + */ +void enableSendPWMByTimer() { +} +/** + * Disables output of the PWM signal of the timer at the timer pin and set it to inactive. + */ +void disableSendPWMByTimer() { +} + +#elif defined(__AVR__) +/********************************************************************************************************************** + * Mapping of AVR boards to AVR timers + * For some CPU's you have the option to switch the timer and the hardware send pin + **********************************************************************************************************************/ +/*************************************** + * Plain AVR CPU's, no boards + ***************************************/ +// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, Nano, etc +#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) || defined(__AVR_ATmega168__) \ + || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega88PB__) +# if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER2) +//#define IR_USE_AVR_TIMER1 // send pin = pin 9 +#define IR_USE_AVR_TIMER2 // send pin = pin 3 +# endif + +// Arduino Mega +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +# if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER2) && !defined(IR_USE_AVR_TIMER3) && !defined(IR_USE_AVR_TIMER4) && !defined(IR_USE_AVR_TIMER5) +//#define IR_USE_AVR_TIMER1 // send pin = pin 11 +#define IR_USE_AVR_TIMER2 // send pin = pin 9 +//#define IR_USE_AVR_TIMER3 // send pin = pin 5 +//#define IR_USE_AVR_TIMER4 // send pin = pin 6 +//#define IR_USE_AVR_TIMER5 // send pin = pin 46 +# endif + +// Leonardo +#elif defined(__AVR_ATmega32U4__) && ! defined(TEENSYDUINO) && ! defined(ARDUINO_AVR_PROMICRO) +# if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER3) && !defined(IR_USE_AVR_TIMER4_HS) +//#define IR_USE_AVR_TIMER1 // send pin = pin 9 +#define IR_USE_AVR_TIMER3 // send pin = pin 5 +//#define IR_USE_AVR_TIMER4_HS // send pin = pin 13 +# endif + +// Nano Every, Uno WiFi Rev2 and similar +#elif defined(__AVR_ATmega808__) || defined(__AVR_ATmega809__) || defined(__AVR_ATmega3208__) || defined(__AVR_ATmega3209__) \ + || defined(__AVR_ATmega1608__) || defined(__AVR_ATmega1609__) || defined(__AVR_ATmega4808__) || defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny1604__) +# if !defined(IR_USE_AVR_TIMER_B) +#define IR_USE_AVR_TIMER_B // send pin = pin 6 on ATmega4809 1 on ATmega4809 +# endif + +#elif defined(__AVR_ATtiny816__) || defined(__AVR_ATtiny1614__) || defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__) // e.g. TinyCore boards +# if !defined(IR_USE_AVR_TIMER_A) && !defined(IR_USE_AVR_TIMER_D) +#define IR_USE_AVR_TIMER_A // use this if you use megaTinyCore, Tone is on TCB and millis() on TCD +//#define IR_USE_AVR_TIMER_D // use this if you use TinyCore +# endif + +// ATmega8u2, ATmega16U2, ATmega32U2, ATmega8 - Timer 2 does not work with existing code below +#elif defined(__AVR_ATmega8U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega8__) +# if !defined(IR_USE_AVR_TIMER1) +#define IR_USE_AVR_TIMER1 // send pin = pin C6 +# endif + +// ATtiny84 +#elif defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny88__) +# if !defined(IR_USE_AVR_TIMER1) +#define IR_USE_AVR_TIMER1 // send pin = pin 6, no tone() available when using ATTinyCore +# endif + +#elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +# if !defined(IR_USE_AVR_TIMER1) +#define IR_USE_AVR_TIMER1 // send pin = pin PB1 / 8 +# endif +#define USE_TIMER_CHANNEL_B + +//ATtiny85, 45, 25 +#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) +# if !defined(IR_USE_AVR_TIMER_TINY0) && !defined(IR_USE_AVR_TIMER_TINY1) +# if defined(ARDUINO_AVR_DIGISPARK) // tested with 16 and 8 MHz +#define IR_USE_AVR_TIMER_TINY0 // send pin = pin 1 +// standard Digispark settings use timer 1 for millis() and micros() +# else +// standard ATTinyCore settings use timer 0 for millis() and micros() +#define IR_USE_AVR_TIMER_TINY1 // send pin = pin 4 +# endif +# endif + +/*************************************** + * SPARKFUN Pro Micro board + ***************************************/ +#elif defined(ARDUINO_AVR_PROMICRO) +# if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER3) && !defined(IR_USE_AVR_TIMER4_HS) +//#define IR_USE_AVR_TIMER1 // send pin = pin 9 +#define IR_USE_AVR_TIMER3 // send pin = pin 5 +//#define IR_USE_AVR_TIMER4_HS // send pin = pin 13 +# endif + +/*************************************** + * TEENSY Boards + ***************************************/ +// Teensy 1.0 +#elif defined(__AVR_AT90USB162__) +# if !defined(IR_USE_AVR_TIMER1) +#define IR_USE_AVR_TIMER1 // send pin = pin 17 +# endif + +// Teensy++ 1.0 & 2.0 +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) +# if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER2) && !defined(IR_USE_AVR_TIMER3) +//#define IR_USE_AVR_TIMER1 // send pin = pin 25 +#define IR_USE_AVR_TIMER2 // send pin = pin 1 +//#define IR_USE_AVR_TIMER3 // send pin = pin 16 +# endif + +// Teensy 2.0 +#elif defined(__AVR_ATmega32U4__) && defined(TEENSYDUINO) +# if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER3) && !defined(IR_USE_AVR_TIMER4_HS) +//#define IR_USE_AVR_TIMER1 // send pin = pin 14 (Teensy 2.0 - physical pin: B5) +//#define IR_USE_AVR_TIMER3 // send pin = pin 9 (Teensy 2.0 - physical pin: C6) +#define IR_USE_AVR_TIMER4_HS // send pin = pin 10 (Teensy 2.0 - physical pin: C7) +# endif + +/*************************************** + * CPU's with MegaCore + ***************************************/ +// MegaCore - ATmega64, ATmega128 +#elif defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) +# if !defined(IR_USE_AVR_TIMER1) +#define IR_USE_AVR_TIMER1 // send pin = pin 13 +# endif + +/*************************************** + * CPU's with MajorCore + ***************************************/ +#elif defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +# if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER3) +#define IR_USE_AVR_TIMER1 // send pin = pin 13 +//#define IR_USE_AVR_TIMER3 // send pin = pin 12 - ATmega162 only +# endif + +/*************************************** + * CPU's with MightyCore + ***************************************/ +// MightyCore - ATmega1284 +#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) +# if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER2) && !defined(IR_USE_AVR_TIMER3) +//#define IR_USE_AVR_TIMER1 // send pin = pin 13 +#define IR_USE_AVR_TIMER2 // send pin = pin 14 +//#define IR_USE_AVR_TIMER3 // send pin = pin 6 +# endif + +// MightyCore - ATmega164, ATmega324, ATmega644 +#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) +# if !defined(IR_USE_AVR_TIMER1) && !defined(IR_USE_AVR_TIMER2) +//#define IR_USE_AVR_TIMER1 // send pin = pin 13 +#define IR_USE_AVR_TIMER2 // send pin = pin 14 +# endif + +// MightyCore - ATmega8535, ATmega16, ATmega32 +#elif defined(__AVR_ATmega8535__) || defined(__AVR_ATmega16__) || defined(__AVR_ATmega32__) +# if !defined(IR_USE_AVR_TIMER1) +#define IR_USE_AVR_TIMER1 // send pin = pin 13 +# endif + +#endif // AVR CPU's +/********************************************************************************************************************** + * End of AVR mapping, start of AVR timers + **********************************************************************************************************************/ +/* + * AVR Timer1 (16 bits) + */ +#if defined(IR_USE_AVR_TIMER1) + +# if defined(TIMSK1) +#define TIMSK TIMSK1 // use the value of TIMSK1 for the statements below +# endif + +void timerEnableReceiveInterrupt() { + TIMSK |= _BV(OCIE1A); +} +void timerDisableReceiveInterrupt() { + TIMSK &= ~_BV(OCIE1A); +} + +# if defined(USE_TIMER_CHANNEL_B) +# if defined(TIMER1_COMPB_vect) +#define TIMER_INTR_NAME TIMER1_COMPB_vect +# elif defined(TIM1_COMPB_vect) +#define TIMER_INTR_NAME TIM1_COMPB_vect +# endif +#else +# if defined(TIMER1_COMPA_vect) +#define TIMER_INTR_NAME TIMER1_COMPA_vect +# elif defined(TIM1_COMPA_vect) +#define TIMER_INTR_NAME TIM1_COMPA_vect +# endif +# endif + +void timerConfigForReceive() { + TCCR1A = 0; + TCCR1B = _BV(WGM12) | _BV(CS10); // CTC mode, no prescaling + OCR1A = (F_CPU * MICROS_PER_TICK) / MICROS_IN_ONE_SECOND; // 16 * 50 = 800 + TCNT1 = 0; +} + +# if defined(SEND_PWM_BY_TIMER) +# if defined(CORE_OC1A_PIN) +#define IR_SEND_PIN CORE_OC1A_PIN // Teensy + +# elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define IR_SEND_PIN 11 // Arduino Mega + +// MightyCore, MegaCore, MajorCore +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_SEND_PIN 13 + +# elif defined(__AVR_ATtiny84__) +#define IR_SEND_PIN 6 + +# elif defined(__AVR_ATtiny88__) +#define IR_SEND_PIN 8 + +# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +/* + * !!! IR_SEND_PIN value must correspond to ENABLE_SEND_PWM_BY_TIMER below !!! + */ +# if defined(USE_TIMER_CHANNEL_B) +#define IR_SEND_PIN PIN_PB1 // OC1BU / PB1 / Pin9 at ATTinyCore +//#define IR_SEND_PIN PIN_PB3 // OC1BV / PB3 / Pin11 at ATTinyCore +//#define IR_SEND_PIN PIN_PB5 // OC1BW / PB5 / Pin13 at ATTinyCore +//#define IR_SEND_PIN PIN_PB7 // OC1BX / PB7 / Pin15 at ATTinyCore +# else +#define IR_SEND_PIN PIN_PB0 // OC1AU / PB1 / Pin8 at ATTinyCore +//#define IR_SEND_PIN PIN_PB2 // OC1AV / PB3 / Pin10 at ATTinyCore +//#define IR_SEND_PIN PIN_PB4 // OC1AW / PB5 / Pin12 at ATTinyCore +//#define IR_SEND_PIN PIN_PB6 // OC1AX / PB6 / Pin14 at ATTinyCore +# endif + +# else +#define IR_SEND_PIN 9 // OC1A Arduino Duemilanove, Diecimila, LilyPad, Sparkfun Pro Micro, Leonardo, MH-ET Tiny88 etc. +# endif // defined(CORE_OC1A_PIN) + +# if defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) +// Clear OC1A/OC1B on Compare Match when up-counting. Set OC1A/OC1B on Compare Match when down counting. +# if defined(USE_TIMER_CHANNEL_B) +void enableSendPWMByTimer() { + TCNT1 = 0; + TCCR1A |= _BV(COM1B1); + TCCR1D |= _BV(OC1BU); // + enable OC1BU as output + //TCNT1 = 0; TCCR1A |= _BV(COM1B1); TCCR1D |= _BV(OC1BV); // + enable OC1BV as output + //TCNT1 = 0; TCCR1A |= _BV(COM1B1); TCCR1D |= _BV(OC1BW); // + enable OC1BW as output + //TCNT1 = 0; TCCR1A |= _BV(COM1B1); TCCR1D |= _BV(OC1BX); // + enable OC1BX as output +} +# else +void disableSendPWMByTimer() { + TCNT1 = 0; + TCCR1A |= _BV(COM1A1); + TCCR1D |= _BV(OC1AU); // + enable OC1BU as output + //TCNT1 = 0; TCCR1A |= _BV(COM1A1); TCCR1D |= _BV(OC1AV); // + enable OC1BV as output + //TCNT1 = 0; TCCR1A |= _BV(COM1A1); TCCR1D |= _BV(OC1AW); // + enable OC1BW as output + //TCNT1 = 0; TCCR1A |= _BV(COM1A1); TCCR1D |= _BV(OC1AX); // + enable OC1BX as output +} + +# endif +void disableSendPWMByTimer() { + TCCR1D = 0; +} +# else +# if defined(USE_TIMER_CHANNEL_B) +void enableSendPWMByTimer() { + TCNT1 = 0; + TCCR1A |= _BV(COM1B1); // Clear OC1A/OC1B on Compare Match when up-counting. Set OC1A/OC1B on Compare Match when counting down. +} +void disableSendPWMByTimer() { + TCCR1A &= ~(_BV(COM1B1)); +} +# else +void enableSendPWMByTimer() { + TCNT1 = 0; + TCCR1A |= _BV(COM1A1); // Clear OC1A/OC1B on Compare Match when up-counting. Set OC1A/OC1B on Compare Match when downcounting. +} +void disableSendPWMByTimer() { + TCCR1A &= ~(_BV(COM1A1)); +} +# endif +# endif + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { + timerDisableReceiveInterrupt(); + +# if (((F_CPU / 2000) / 38) < 256) + const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of Phase Correct PWM + TCCR1A = _BV(WGM11); // PWM, Phase Correct, Top is ICR1 + TCCR1B = _BV(WGM13) | _BV(CS10); // CS10 -> no prescaling + ICR1 = tPWMWrapValue - 1; +# if defined(USE_TIMER_CHANNEL_B) + OCR1B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; +# else + OCR1A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; +# endif + TCNT1 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible +# else + const uint16_t tPWMWrapValue = ((F_CPU / 8) / 2000) / (aFrequencyKHz); // 2000 instead of 1000 because of Phase Correct PWM + TCCR1A = _BV(WGM11);// PWM, Phase Correct, Top is ICR1 + TCCR1B = _BV(WGM13) | _BV(CS11);// CS11 -> Prescaling by 8 + ICR1 = tPWMWrapValue - 1; +# if defined(USE_TIMER_CHANNEL_B) + OCR1A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; +# else + OCR1A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; +# endif + TCNT1 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible +# endif +} +# endif // defined(SEND_PWM_BY_TIMER) + +/* + * AVR Timer2 (8 bits) // Tone timer on Uno + */ +#elif defined(IR_USE_AVR_TIMER2) + +void timerEnableReceiveInterrupt() { + TIMSK2 = _BV(OCIE2B); // Output Compare Match A Interrupt Enable +} +void timerDisableReceiveInterrupt() { + TIMSK2 = 0; +} +#define TIMER_INTR_NAME TIMER2_COMPB_vect // We use TIMER2_COMPB_vect to be compatible with tone() library + +#define TIMER_COUNT_TOP (F_CPU * MICROS_PER_TICK / MICROS_IN_ONE_SECOND) + +void timerConfigForReceive() { +# if (TIMER_COUNT_TOP < 256) + TCCR2A = _BV(WGM21); + TCCR2B = _BV(CS20); + OCR2A = TIMER_COUNT_TOP; + OCR2B = TIMER_COUNT_TOP; + TCNT2 = 0; +# else + TCCR2A = _BV(WGM21); + TCCR2B = _BV(CS21); + OCR2A = TIMER_COUNT_TOP / 8; + OCR2B = TIMER_COUNT_TOP / 8; + TCNT2 = 0; +# endif +} + +# if defined(SEND_PWM_BY_TIMER) +# if defined(CORE_OC2B_PIN) +#define IR_SEND_PIN CORE_OC2B_PIN // Teensy + +# elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define IR_SEND_PIN 9 // Arduino Mega + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) +#define IR_SEND_PIN 14 // MightyCore, MegaCore + +# else +#define IR_SEND_PIN 3 // Arduino Duemilanove, Diecimila, LilyPad, etc +# endif // defined(CORE_OC2B_PIN) + +void enableSendPWMByTimer() { + TCNT2 = 0; + TCCR2A |= _BV(COM2B1); // Clear OC2B on Compare Match +} +void disableSendPWMByTimer() { + TCCR2A &= ~(_BV(COM2B1)); // Normal port operation, OC2B disconnected. +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { + timerDisableReceiveInterrupt(); + +# if (((F_CPU / 2000) / 38) < 256) + /* + * tPWMWrapValue is 210.52 for 38 kHz, 17.58 for 455 kHz @16 MHz clock. + * 210 -> 38.095 kHz, 17 -> 470.588 kHz @16 MHz clock. + * We use 2000 instead of 1000 in the formula, because of Phase Correct PWM. + */ + const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); + TCCR2A = _BV(WGM20); // PWM, Phase Correct, Top is OCR2A + TCCR2B = _BV(WGM22) | _BV(CS20); // CS20 -> no prescaling + OCR2A = tPWMWrapValue - 1; // The top value for the timer. The modulation frequency will be F_CPU / 2 / (OCR2A + 1). + OCR2B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; + TCNT2 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible +# else + const uint16_t tPWMWrapValue = ((F_CPU / 8) / 2000) / (aFrequencyKHz); // 2000 instead of 1000 because of Phase Correct PWM + TCCR2A = _BV(WGM20);// PWM, Phase Correct, Top is OCR2A + TCCR2B = _BV(WGM22) | _BV(CS21);// CS21 -> Prescaling by 8 + OCR2A = tPWMWrapValue - 1; + OCR2B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; + TCNT2 = 0;// not really required, since we have an 8 bit counter, but makes the signal more reproducible +# endif +} +# endif // defined(SEND_PWM_BY_TIMER) + +/* + * AVR Timer3 (16 bits) + */ +#elif defined(IR_USE_AVR_TIMER3) + +void timerEnableReceiveInterrupt() { + TIMSK3 = _BV(OCIE3B); +} +void timerDisableReceiveInterrupt() { + TIMSK3 = 0; +} +#define TIMER_INTR_NAME TIMER3_COMPB_vect + +void timerConfigForReceive() { + TCCR3A = 0; + TCCR3B = _BV(WGM32) | _BV(CS30); + OCR3A = F_CPU * MICROS_PER_TICK / MICROS_IN_ONE_SECOND; + OCR3B = F_CPU * MICROS_PER_TICK / MICROS_IN_ONE_SECOND; + TCNT3 = 0; +} + +# if defined(SEND_PWM_BY_TIMER) +# if defined(CORE_OC3A_PIN) +#define IR_SEND_PIN CORE_OC3A_PIN // Teensy + +# elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) \ +|| defined(__AVR_ATmega32U4__) || defined(ARDUINO_AVR_PROMICRO) +#define IR_SEND_PIN 5 // Arduino Mega, Arduino Leonardo, Sparkfun Pro Micro + +# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) +#define IR_SEND_PIN 6 // MightyCore, MegaCore + +# else +#error Please add OC3A pin number here +# endif + +void enableSendPWMByTimer() { + TCNT3 = 0; + TCCR3A |= _BV(COM3A1); +} +void disableSendPWMByTimer() { + TCCR3A &= ~(_BV(COM3A1)); +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { +#if F_CPU > 16000000 +#error "Creating timer PWM with timer 3 is not supported for F_CPU > 16 MHz" +#endif + timerDisableReceiveInterrupt(); + + const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of Phase Correct PWM + TCCR3A = _BV(WGM31); + TCCR3B = _BV(WGM33) | _BV(CS30); // PWM, Phase Correct, ICRn as TOP, complete period is double of tPWMWrapValue + ICR3 = tPWMWrapValue - 1; + OCR3A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; + TCNT3 = 0; // required, since we have an 16 bit counter +} +# endif // defined(SEND_PWM_BY_TIMER) + +/* + * AVR Timer4 (16 bits) + */ +#elif defined(IR_USE_AVR_TIMER4) +void timerEnableReceiveInterrupt() { + TIMSK4 = _BV(OCIE4A); +} +void timerDisableReceiveInterrupt() { + TIMSK4 = 0; +} +#define TIMER_INTR_NAME TIMER4_COMPA_vect + +void timerConfigForReceive() { + TCCR4A = 0; + TCCR4B = _BV(WGM42) | _BV(CS40); + OCR4A = F_CPU * MICROS_PER_TICK / MICROS_IN_ONE_SECOND; + TCNT4 = 0; +} + +# if defined(SEND_PWM_BY_TIMER) +# if defined(CORE_OC4A_PIN) +#define IR_SEND_PIN CORE_OC4A_PIN +# elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define IR_SEND_PIN 6 // Arduino Mega +# else +#error Please add OC4A pin number here +# endif + +void enableSendPWMByTimer() { + TCNT4 = 0; + TCCR4A |= _BV(COM4A1); +} +void disableSendPWMByTimer() { + TCCR4A &= ~(_BV(COM4A1)); +} + +void timerConfigForSend(uint16_t aFrequencyKHz) { +#if F_CPU > 16000000 +#error "Creating timer PWM with timer 4 is not supported for F_CPU > 16 MHz" +#endif + timerDisableReceiveInterrupt(); + const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of Phase Correct PWM + TCCR4A = _BV(WGM41); + TCCR4B = _BV(WGM43) | _BV(CS40); + ICR4 = tPWMWrapValue - 1; + OCR4A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; + TCNT4 = 0; // required, since we have an 16 bit counter +} +# endif // defined(SEND_PWM_BY_TIMER) + +/* + * AVR Timer4 (10 bits, high speed option) + */ +#elif defined(IR_USE_AVR_TIMER4_HS) + +void timerEnableReceiveInterrupt() { + TIMSK4 = _BV(TOIE4); +} +void timerDisableReceiveInterrupt() { + TIMSK4 = 0; +} +#define TIMER_INTR_NAME TIMER4_OVF_vect + +void timerConfigForReceive() { + TCCR4A = 0; + TCCR4B = _BV(CS40); + TCCR4C = 0; + TCCR4D = 0; + TCCR4E = 0; + TC4H = (F_CPU * MICROS_PER_TICK / MICROS_IN_ONE_SECOND) >> 8; + OCR4C = (F_CPU * MICROS_PER_TICK / MICROS_IN_ONE_SECOND) & 255; + TC4H = 0; + TCNT4 = 0; +} + +# if defined(SEND_PWM_BY_TIMER) +# if defined(CORE_OC4A_PIN) +#define IR_SEND_PIN CORE_OC4A_PIN // Teensy 2.0 +# elif defined(ARDUINO_AVR_PROMICRO) +#define IR_SEND_PIN 5 // Sparkfun Pro Micro +# elif defined(__AVR_ATmega32U4__) +#define IR_SEND_PIN 13 // Leonardo +# else +#error Please add OC4A pin number here +# endif + +# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro +void enableSendPWMByTimer() { + TCNT4 = 0; + TCCR4A |= _BV(COM4A0); // Use complementary OC4A output on pin 5 +} +void disableSendPWMByTimer() { + TCCR4A &= ~(_BV(COM4A0)); // (Pro Micro does not map PC7 (32/ICP3/CLK0/OC4A) +} +// of ATmega32U4 ) +# else +void enableSendPWMByTimer() { + TCNT4 = 0; + TCCR4A |= _BV(COM4A1); + DDRC |= 1 << 7; +} +void disableSendPWMByTimer() { + TCCR4A &= ~(_BV(COM4A1)); +} +# endif + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { +#if F_CPU > 16000000 +#error "Creating timer PWM with timer 4 HS is not supported for F_CPU > 16 MHz" +#endif + timerDisableReceiveInterrupt(); + + const uint16_t tPWMWrapValue = ((F_CPU / 2000) / (aFrequencyKHz)) - 1; // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of Phase Correct PWM + TCCR4A = (1 << PWM4A); + TCCR4B = _BV(CS40); + TCCR4C = 0; + TCCR4D = (1 << WGM40); + TCCR4E = 0; + TC4H = tPWMWrapValue >> 8; + OCR4C = tPWMWrapValue; + TC4H = (tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT / 100) >> 8; + OCR4A = (tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT / 100) & 255; + TCNT4 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible +} +# endif // defined(SEND_PWM_BY_TIMER) + +/* + * AVR Timer5 (16 bits) + */ +#elif defined(IR_USE_AVR_TIMER5) + +void timerEnableReceiveInterrupt() { + TIMSK5 = _BV(OCIE5A); +} +void timerDisableReceiveInterrupt() { + TIMSK5 = 0; +} +#define TIMER_INTR_NAME TIMER5_COMPA_vect + +void timerConfigForReceive() { + TCCR5A = 0; + TCCR5B = _BV(WGM52) | _BV(CS50); + OCR5A = F_CPU * MICROS_PER_TICK / MICROS_IN_ONE_SECOND; + TCNT5 = 0; +} + +# if defined(SEND_PWM_BY_TIMER) +# if defined(CORE_OC5A_PIN) +#define IR_SEND_PIN CORE_OC5A_PIN +# elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define IR_SEND_PIN 46 // Arduino Mega +# else +#error Please add OC5A pin number here +# endif + +void enableSendPWMByTimer() { + TCNT5 = 0; + TCCR5A |= _BV(COM5A1); +} +void disableSendPWMByTimer() { + TCCR5A &= ~(_BV(COM5A1)); +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { +#if F_CPU > 16000000 +#error "Creating timer PWM with timer 5 is not supported for F_CPU > 16 MHz" +#endif + timerDisableReceiveInterrupt(); + + const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of Phase Correct PWM + TCCR5A = _BV(WGM51); + TCCR5B = _BV(WGM53) | _BV(CS50); + ICR5 = tPWMWrapValue - 1; + OCR5A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; + TCNT5 = 0; // required, since we have an 16 bit counter +} +# endif // defined(SEND_PWM_BY_TIMER) + +/* + * AVR Timer0 for ATtinies (8 bits) + */ +#elif defined(IR_USE_AVR_TIMER_TINY0) + +void timerEnableReceiveInterrupt() { + TIMSK |= _BV(OCIE0A); +} +void timerDisableReceiveInterrupt() { + TIMSK &= ~(_BV(OCIE0A)); +} +#define TIMER_INTR_NAME TIMER0_COMPA_vect + +#define TIMER_COUNT_TOP (F_CPU * MICROS_PER_TICK / MICROS_IN_ONE_SECOND) + +void timerConfigForReceive() { +# if (TIMER_COUNT_TOP < 256) + TCCR0A = _BV(WGM01); // CTC, Top is OCR0A + TCCR0B = _BV(CS00);// No prescaling + OCR0A = TIMER_COUNT_TOP; + TCNT0 = 0; +# else + TCCR0A = _BV(WGM01); + TCCR0B = _BV(CS01); // prescaling by 8 + OCR0A = TIMER_COUNT_TOP / 8; + TCNT0 = 0; +# endif +} + +# if defined(SEND_PWM_BY_TIMER) +#define IR_SEND_PIN 1 + +void enableSendPWMByTimer() { + TCNT0 = 0; + TCCR0A |= _BV(COM0B1); +} +void disableSendPWMByTimer() { + TCCR0A &= ~(_BV(COM0B1)); +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { +#if F_CPU > 16000000 +#error "Creating timer PWM with timer TINY0 is not supported for F_CPU > 16 MHz" +#endif + timerDisableReceiveInterrupt(); + + const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of Phase Correct PWM + TCCR0A = _BV(WGM00); // PWM, Phase Correct, Top is OCR0A + TCCR0B = _BV(WGM02) | _BV(CS00); // CS00 -> no prescaling + OCR0A = tPWMWrapValue - 1; + OCR0B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; + TCNT0 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible +} +# endif // defined(SEND_PWM_BY_TIMER) + +/* + * AVR Timer1 for ATtinies (8 bits) + */ +#elif defined(IR_USE_AVR_TIMER_TINY1) + +void timerEnableReceiveInterrupt() { + TIMSK |= _BV(OCIE1B); +} +void timerDisableReceiveInterrupt() { + TIMSK &= ~(_BV(OCIE1B)); +} +#define TIMER_INTR_NAME TIMER1_COMPB_vect + +#define TIMER_COUNT_TOP (F_CPU * MICROS_PER_TICK / MICROS_IN_ONE_SECOND) + +void timerConfigForReceive() { +# if (TIMER_COUNT_TOP < 256) + TCCR1 = _BV(CTC1) | _BV(CS10); // Clear Timer/Counter on Compare Match, Top is OCR1C, No prescaling + GTCCR = 0;// normal, non-PWM mode + OCR1C = TIMER_COUNT_TOP; + TCNT1 = 0; +# else + TCCR1 = _BV(CTC1) | _BV(CS12); // Clear Timer/Counter on Compare Match, Top is OCR1C, prescaling by 8 + GTCCR = 0; // normal, non-PWM mode + OCR1C = TIMER_COUNT_TOP / 8; + TCNT1 = 0; +# endif +} + +# if defined(SEND_PWM_BY_TIMER) +#define IR_SEND_PIN 4 + +void enableSendPWMByTimer() { + TCNT1 = 0; + GTCCR |= _BV(PWM1B) | _BV(COM1B0); // Enable pin 4 PWM output (PB4 - Arduino D4) +} +void disableSendPWMByTimer() { + GTCCR &= ~(_BV(PWM1B) | _BV(COM1B0)); +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { + timerDisableReceiveInterrupt(); + +# if (((F_CPU / 1000) / 38) < 256) + const uint16_t tPWMWrapValue = (F_CPU / 1000) / (aFrequencyKHz); // 421 @16 MHz, 26 @1 MHz and 38 kHz + TCCR1 = _BV(CTC1) | _BV(CS10);// CTC1 = 1: TOP value set to OCR1C, CS10 No Prescaling + OCR1C = tPWMWrapValue - 1; + OCR1B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; + TCNT1 = 0;// not really required, since we have an 8 bit counter, but makes the signal more reproducible + GTCCR = _BV(PWM1B) | _BV(COM1B0);// PWM1B = 1: Enable PWM for OCR1B, COM1B0 Clear on compare match +# else + const uint16_t tPWMWrapValue = ((F_CPU / 2) / 1000) / (aFrequencyKHz); // 210 for 16 MHz and 38 kHz + TCCR1 = _BV(CTC1) | _BV(CS11); // CTC1 = 1: TOP value set to OCR1C, CS11 Prescaling by 2 + OCR1C = tPWMWrapValue - 1; + OCR1B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; + TCNT1 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible + GTCCR = _BV(PWM1B) | _BV(COM1B0); // PWM1B = 1: Enable PWM for OCR1B, COM1B0 Clear on compare match +# endif +} +# endif // defined(SEND_PWM_BY_TIMER) + +/* + * AVR TimerA for TinyCore 32 (16 bits) + */ +#elif defined(IR_USE_AVR_TIMER_A) +#define TIMER_REQUIRES_RESET_INTR_PENDING +void timerResetInterruptPending() { + TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm; +} +void timerEnableReceiveInterrupt() { + TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm; +} +void timerDisableReceiveInterrupt() { + TCA0.SINGLE.INTCTRL &= ~(TCA_SINGLE_OVF_bm); +} +#define TIMER_INTR_NAME TCA0_OVF_vect +// For MegaTinyCore: +// TCB1 is used by Tone() +// TCB2 is used by Servo, but we cannot hijack the ISR, so we must use a dedicated timer for the 20 ms interrupt +// TCB3 is used by millis() +// Must use TCA0, since TCBx have only prescaler %2. Use single (16bit) mode, because it seems to be easier :-) +void timerConfigForReceive() { + TCA0.SINGLE.CTRLD = 0; // Single mode - required at least for MegaTinyCore + TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_NORMAL_gc; // Normal mode, top = PER + TCA0.SINGLE.PER = (F_CPU / MICROS_IN_ONE_SECOND) * MICROS_PER_TICK; // 800 at 16 MHz + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc | TCA_SINGLE_ENABLE_bm; // set prescaler to 1 and enable timer +} + +# if defined(SEND_PWM_BY_TIMER) +#error "No support for hardware PWM generation for ATtiny3216/17 etc." +# endif // defined(SEND_PWM_BY_TIMER) + +/* + * AVR TimerB (8 bits) for ATmega4809 (Nano Every, Uno WiFi Rev2) + */ +#elif defined(IR_USE_AVR_TIMER_B) + +// ATmega4809 TCB0 +#define TIMER_REQUIRES_RESET_INTR_PENDING +void timerResetInterruptPending() { + TCB0.INTFLAGS = TCB_CAPT_bm; +} +void timerEnableReceiveInterrupt() { + TCB0.INTCTRL = TCB_CAPT_bm; +} +void timerDisableReceiveInterrupt() { + TCB0.INTCTRL &= ~(TCB_CAPT_bm); +} +#define TIMER_INTR_NAME TCB0_INT_vect + +void timerConfigForReceive() { + TCB0.CTRLB = (TCB_CNTMODE_INT_gc); // Periodic interrupt mode + TCB0.CCMP = ((F_CPU * MICROS_PER_TICK) / MICROS_IN_ONE_SECOND); + TCB0.INTFLAGS = TCB_CAPT_bm; // reset interrupt flags + TCB0.CTRLA = (TCB_CLKSEL_CLKDIV1_gc) | (TCB_ENABLE_bm); +} + +# if defined(SEND_PWM_BY_TIMER) +# if defined(__AVR_ATmega4808__) || defined(__AVR_ATmega4809__) +#define IR_SEND_PIN 6 // PF4 on ATmega4809 / Nano Every (see pins_arduino.h digital_pin_to_timer) +# else +#error SEND_PWM_BY_TIMER not yet supported for this CPU +# endif + +void enableSendPWMByTimer() { + TCB0.CNT = 0; + TCB0.CTRLB |= TCB_CCMPEN_bm; // set Compare/Capture Output Enable +} +void disableSendPWMByTimer() { + TCB0.CTRLB &= ~(TCB_CCMPEN_bm); +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { +#if F_CPU > 16000000 + // we have only prescaler 2 or must take clock of timer A (which is non deterministic) +#error "Creating timer PWM with timer TCB0 is not possible for F_CPU > 16 MHz" +#endif + timerDisableReceiveInterrupt(); + + const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of using CLK / 2 + TCB0.CTRLB = TCB_CNTMODE_PWM8_gc; // 8 bit PWM mode + TCB0.CCMPL = tPWMWrapValue - 1; // Period of 8 bit PWM + TCB0.CCMPH = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; // Duty cycle of waveform of 8 bit PWM + TCB0.CTRLA = (TCB_CLKSEL_CLKDIV2_gc) | (TCB_ENABLE_bm); // use CLK / 2 + TCB0.CNT = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible +} +# endif // defined(SEND_PWM_BY_TIMER) + +/* + * AVR TimerD for TinyCore 32 (16 bits) + */ +#elif defined(IR_USE_AVR_TIMER_D) + +#define TIMER_REQUIRES_RESET_INTR_PENDING +void timerResetInterruptPending() { + TCD0.INTFLAGS = TCD_OVF_bm; +} +void timerEnableReceiveInterrupt() { + TCD0.INTCTRL = TCD_OVF_bm; +} +void timerDisableReceiveInterrupt() { + TCD0.INTCTRL = 0; +} +#define TIMER_INTR_NAME TCD0_OVF_vect + +void timerConfigForReceive() { + TCD0.CTRLA = 0; // reset enable bit in order to unprotect the other bits + TCD0.CTRLB = TCD_WGMODE_ONERAMP_gc; // must be set since it is used by PWM +// TCD0.CMPBSET = 80; + TCD0.CMPBCLR = ((F_CPU * MICROS_PER_TICK) / MICROS_IN_ONE_SECOND) - 1; + + _PROTECTED_WRITE(TCD0.FAULTCTRL, 0); // must disable WOA output at pin 13/PA4 + + TCD0.INTFLAGS = TCD_OVF_bm; // reset interrupt flags + // check enable ready +// while ((TCD0.STATUS & TCD_ENRDY_bm) == 0); // Wait for Enable Ready to be high - I guess it is not required + // enable timer - this locks the other bits and static registers and activates values in double buffered registers + TCD0.CTRLA = TCD_ENABLE_bm | TCD_CLKSEL_SYSCLK_gc | TCD_CNTPRES_DIV1_gc; // System clock, no prescale, no synchronization prescaler +} + +# if defined(SEND_PWM_BY_TIMER) +#define IR_SEND_PIN 13 + +void timerEnableSendPWM() { + TCD0.CTRLA = 0; // reset enable bit in order to unprotect the other bits + _PROTECTED_WRITE(TCD0.FAULTCTRL, FUSE_CMPAEN_bm); // enable WOA output at pin 13/PA4 +// _PROTECTED_WRITE(TCD0.FAULTCTRL, FUSE_CMPAEN_bm | FUSE_CMPBEN_bm); // enable WOA + WOB output pins at 13/PA4 + 14/PA5 + TCD0.CTRLA = TCD_ENABLE_bm | TCD_CLKSEL_SYSCLK_gc | TCD_CNTPRES_DIV1_gc; // System clock, no prescale, no synchronization prescaler +} + +void enableSendPWMByTimer() { + timerEnableSendPWM(); +} +void disableSendPWMByTimer() { + TCD0.CTRLA = 0; // do not disable output, disable complete timer +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { + timerDisableReceiveInterrupt(); + + const uint16_t tPWMWrapValue = (F_CPU / 1000) / (aFrequencyKHz); // 526,31 for 38 kHz @20 MHz clock + // use one ramp mode and overflow interrupt + TCD0.CTRLA = 0; // reset enable bit in order to unprotect the other bits +// while ((TCD0.STATUS & TCD_ENRDY_bm) == 0); // Wait for Enable Ready to be high - I guess it is not required + TCD0.CTRLB = TCD_WGMODE_ONERAMP_gc; // must be set since it is used by PWM + TCD0.CTRLC = 0; // reset WOx output settings +// TCD0.CMPBSET = 80; + TCD0.CMPBCLR = tPWMWrapValue - 1; + + // Generate duty cycle signal for debugging etc. + TCD0.CMPASET = 0; + TCD0.CMPACLR = (tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT / 100) - 1; // duty cycle for WOA + + TCD0.INTFLAGS = TCD_OVF_bm; // reset interrupt flags + TCD0.INTCTRL = TCD_OVF_bm; // overflow interrupt + // Do not enable timer, this is done at timerEnablSendPWM() +} +# endif // defined(SEND_PWM_BY_TIMER) + +#else +#error Internal code configuration error, no timer functions implemented for this AVR CPU / board +#endif //defined(IR_USE_AVR_TIMER*) +/********************************************************************************************************************** + * End of AVR timers + **********************************************************************************************************************/ + +/********************************************** + * Uno R4 boards + **********************************************/ +#elif defined(ARDUINO_ARCH_RENESAS) +#include "FspTimer.h" +FspTimer s50usTimer; + +// Undefine ISR, because we register/call the plain function IRReceiveTimerInterruptHandler() +# if defined(ISR) +#undef ISR +# endif + +// callback method used by timer +void IRTimerInterruptHandlerHelper(timer_callback_args_t __attribute((unused)) *p_args) { + IRReceiveTimerInterruptHandler(); +} +void timerEnableReceiveInterrupt() { +// s50usTimer.enable_overflow_irq(); + s50usTimer.start(); +} +void timerDisableReceiveInterrupt() { +// s50usTimer.disable_overflow_irq(); + s50usTimer.stop(); // May save power +} + +void timerConfigForReceive() { + uint8_t tTimerType = GPT_TIMER; + int8_t tIndex = FspTimer::get_available_timer(tTimerType); // Get first unused channel. Here we need the address of tTimerType + if (tIndex < 0 || tTimerType != GPT_TIMER) { + // here we found no unused GPT channel + tIndex = FspTimer::get_available_timer(tTimerType, true); // true to force use of already used PWM channel. Sets "force_pwm_reserved" if timer found + if (tIndex < 0) { + // If we already get an tIndex < 0 we have an error, but do not know how to handle :-( + return; + } + } + s50usTimer.begin(TIMER_MODE_PERIODIC, tTimerType, tIndex, MICROS_IN_ONE_SECOND / MICROS_PER_TICK, 0.0, + IRTimerInterruptHandlerHelper); + s50usTimer.setup_overflow_irq(); + s50usTimer.open(); // In turn calls R_GPT_Enable() + s50usTimer.stop(); // May save power +} + +# if defined(SEND_PWM_BY_TIMER) +#error PWM generation by hardware not yet implemented for Arduino Uno R4 +// Not yet implemented +void enableSendPWMByTimer() { +} +void disableSendPWMByTimer() { +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { +# if defined(IR_SEND_PIN) +# else +# endif +} +# endif + +/********************************************** + * Teensy 3.0 / Teensy 3.1 / Teensy 3.2 boards + **********************************************/ +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) + +// Special carrier modulator timer for Teensy 3.0 / Teensy 3.1 / Teensy 3.2 +#define TIMER_REQUIRES_RESET_INTR_PENDING +void timerResetInterruptPending() { + uint8_t tmp __attribute__((unused)) = CMT_MSC; + CMT_CMD2 = 30; +} +void timerEnableReceiveInterrupt() { + NVIC_ENABLE_IRQ(IRQ_CMT); + NVIC_SET_PRIORITY(IRQ_CMT, 48); +} +void timerDisableReceiveInterrupt() { + NVIC_DISABLE_IRQ(IRQ_CMT); +} + +#define TIMER_INTR_NAME cmt_isr +# if defined(ISR) +#undef ISR +# endif +#define ISR(f) void f(void) + +#define CMT_PPS_DIV ((F_BUS + 7999999) / 8000000) +# if F_BUS < 8000000 +#error IRremote requires at least 8 MHz on Teensy 3.x +# endif + +void timerConfigForReceive() { + SIM_SCGC4 |= SIM_SCGC4_CMT; + CMT_PPS = CMT_PPS_DIV - 1; + CMT_CGH1 = 1; + CMT_CGL1 = 1; + CMT_CMD1 = 0; + CMT_CMD2 = 30; + CMT_CMD3 = 0; + CMT_CMD4 = (F_BUS / 160000 + CMT_PPS_DIV / 2) / CMT_PPS_DIV - 31; + CMT_OC = 0; + CMT_MSC = 0x03; +} + +# if defined(SEND_PWM_BY_TIMER) +#define IR_SEND_PIN 5 + +void enableSendPWMByTimer() { + do { + CORE_PIN5_CONFIG = PORT_PCR_MUX(2) | PORT_PCR_DSE | PORT_PCR_SRE; + } while (0); +} +void disableSendPWMByTimer() { + do { + CORE_PIN5_CONFIG = PORT_PCR_MUX(1) | PORT_PCR_DSE | PORT_PCR_SRE; + } while (0); +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { + timerDisableReceiveInterrupt(); // TODO really required here? Do we have a common resource for Teensy3.0, 3.1 +# if defined(IR_SEND_PIN) + pinMode(IR_SEND_PIN, OUTPUT); +# else + pinMode(IrSender.sendPin, OUTPUT); +# endif + + SIM_SCGC4 |= SIM_SCGC4_CMT; + SIM_SOPT2 |= SIM_SOPT2_PTD7PAD; + CMT_PPS = CMT_PPS_DIV - 1; + CMT_CGH1 = ((F_BUS / CMT_PPS_DIV / 3000) + ((aFrequencyKHz) / 2)) / (aFrequencyKHz); + CMT_CGL1 = ((F_BUS / CMT_PPS_DIV / 1500) + ((aFrequencyKHz) / 2)) / (aFrequencyKHz); + CMT_CMD1 = 0; + CMT_CMD2 = 30; + CMT_CMD3 = 0; + CMT_CMD4 = 0; + CMT_OC = 0x60; + CMT_MSC = 0x01; +} +# endif // defined(SEND_PWM_BY_TIMER) + +/*************************************** + * Teensy-LC board + ***************************************/ +#elif defined(__MKL26Z64__) + +// defines for TPM1 timer on Teensy-LC +#define TIMER_REQUIRES_RESET_INTR_PENDING +void timerResetInterruptPending() { + FTM1_SC |= FTM_SC_TOF; +} +void timerEnableReceiveInterrupt() { + NVIC_ENABLE_IRQ(IRQ_FTM1); + NVIC_SET_PRIORITY(IRQ_FTM1, 0); +} +void timerDisableReceiveInterrupt() { + NVIC_DISABLE_IRQ(IRQ_FTM1); +} +#define TIMER_INTR_NAME ftm1_isr +# if defined(ISR) +#undef ISR +# endif +#define ISR(f) void f(void) + +void timerConfigForReceive() { + SIM_SCGC6 |= SIM_SCGC6_TPM1; + FTM1_SC = 0; + FTM1_CNT = 0; + FTM1_MOD = (F_PLL / 40000) - 1; + FTM1_C0V = 0; + FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0) | FTM_SC_TOF | FTM_SC_TOIE; +} + +# if defined(SEND_PWM_BY_TIMER) +#define IR_SEND_PIN 16 + +void enableSendPWMByTimer() { + FTM1_CNT = 0; + CORE_PIN16_CONFIG = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; +} +void disableSendPWMByTimer() { + CORE_PIN16_CONFIG = PORT_PCR_MUX(1) | PORT_PCR_SRE; +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { + timerDisableReceiveInterrupt(); +# if defined(IR_SEND_PIN) + pinMode(IR_SEND_PIN, OUTPUT); +# else + pinMode(IrSender.sendPin, OUTPUT); +# endif + + SIM_SCGC6 |= SIM_SCGC6_TPM1; + FTM1_SC = 0; + FTM1_CNT = 0; + FTM1_MOD = ((F_PLL / 2000) / aFrequencyKHz) - 1; + FTM1_C0V = ((F_PLL / 6000) / aFrequencyKHz) - 1; + FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); +} +# endif // defined(SEND_PWM_BY_TIMER) + +/*************************************** + * Teensy 4.0, 4.1, MicroMod boards + ***************************************/ +#elif defined(__IMXRT1062__) +// forward declare ISR function (will be implemented by IRReceive.hpp) +void pwm1_3_isr(); + +// defines for FlexPWM1 timer on Teensy 4 +#define TIMER_REQUIRES_RESET_INTR_PENDING +void timerResetInterruptPending() { + FLEXPWM1_SM3STS = FLEXPWM_SMSTS_RF; +} +void timerEnableReceiveInterrupt() { + attachInterruptVector(IRQ_FLEXPWM1_3, pwm1_3_isr); + FLEXPWM1_SM3STS = FLEXPWM_SMSTS_RF; + FLEXPWM1_SM3INTEN = FLEXPWM_SMINTEN_RIE; + NVIC_ENABLE_IRQ (IRQ_FLEXPWM1_3), NVIC_SET_PRIORITY(IRQ_FLEXPWM1_3, 48); +} +void timerDisableReceiveInterrupt() { + NVIC_DISABLE_IRQ (IRQ_FLEXPWM1_3); +} +#define TIMER_INTR_NAME pwm1_3_isr +# if defined(ISR) +#undef ISR +# endif +#define ISR(f) void (f)(void) + +void timerConfigForReceive() { + uint32_t period = (float) F_BUS_ACTUAL * (float) (MICROS_PER_TICK) * 0.0000005f; + uint32_t prescale = 0; + while (period > 32767) { + period = period >> 1; + if (prescale < 7) + prescale++; + } + FLEXPWM1_FCTRL0 |= FLEXPWM_FCTRL0_FLVL(8); + FLEXPWM1_FSTS0 = 0x0008; + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(8); + FLEXPWM1_SM3CTRL2 = FLEXPWM_SMCTRL2_INDEP; + FLEXPWM1_SM3CTRL = FLEXPWM_SMCTRL_HALF | FLEXPWM_SMCTRL_PRSC(prescale); + FLEXPWM1_SM3INIT = -period; + FLEXPWM1_SM3VAL0 = 0; + FLEXPWM1_SM3VAL1 = period; + FLEXPWM1_SM3VAL2 = 0; + FLEXPWM1_SM3VAL3 = 0; + FLEXPWM1_SM3VAL4 = 0; + FLEXPWM1_SM3VAL5 = 0; + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_LDOK(8) | FLEXPWM_MCTRL_RUN(8); +} + +# if defined(SEND_PWM_BY_TIMER) +#define IR_SEND_PIN 7 +void enableSendPWMByTimer() { + FLEXPWM1_OUTEN |= FLEXPWM_OUTEN_PWMA_EN(8); + IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_00 = 6; +} + +void disableSendPWMByTimer() { + IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_00 = 5; + FLEXPWM1_OUTEN &= ~FLEXPWM_OUTEN_PWMA_EN(8); +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { + timerDisableReceiveInterrupt(); +# if defined(IR_SEND_PIN) + pinMode(IR_SEND_PIN, OUTPUT); +# else + pinMode(IrSender.sendPin, OUTPUT); +# endif + + uint32_t period = (float) F_BUS_ACTUAL / (float) ((aFrequencyKHz) * 2000); + uint32_t prescale = 0; + while (period > 32767) { + period = period >> 1; + if (prescale < 7) + prescale++; + } + FLEXPWM1_FCTRL0 |= FLEXPWM_FCTRL0_FLVL(8); + FLEXPWM1_FSTS0 = 0x0008; + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(8); + FLEXPWM1_SM3CTRL2 = FLEXPWM_SMCTRL2_INDEP; + FLEXPWM1_SM3CTRL = FLEXPWM_SMCTRL_HALF | FLEXPWM_SMCTRL_PRSC(prescale); + FLEXPWM1_SM3INIT = -period; + FLEXPWM1_SM3VAL0 = 0; + FLEXPWM1_SM3VAL1 = period; + FLEXPWM1_SM3VAL2 = -(period / 3); + FLEXPWM1_SM3VAL3 = period / 3; + FLEXPWM1_SM3VAL4 = 0; + FLEXPWM1_SM3VAL5 = 0; + FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_LDOK(8) | FLEXPWM_MCTRL_RUN(8); +} +# endif // defined(SEND_PWM_BY_TIMER) + +/********************************************************** + * ESP8266 boards + **********************************************************/ +#elif defined(ESP8266) +# if defined(SEND_PWM_BY_TIMER) +#error "No support for hardware PWM generation for ESP8266" +# endif // defined(SEND_PWM_BY_TIMER) + +// Undefine ISR, because we register/call the plain function IRReceiveTimerInterruptHandler() +# if defined(ISR) +#undef ISR +# endif + +void timerEnableReceiveInterrupt() { + timer1_attachInterrupt(&IRReceiveTimerInterruptHandler); // enables interrupt too +} +void timerDisableReceiveInterrupt() { + timer1_detachInterrupt(); // disables interrupt too +} + +void timerConfigForReceive() { + timer1_isr_init(); + /* + * TIM_DIV1 = 0, //80MHz (80 ticks/us - 104857.588 us max) + * TIM_DIV16 = 1, //5MHz (5 ticks/us - 1677721.4 us max) + * TIM_DIV256 = 3 //312.5Khz (1 tick = 3.2us - 26843542.4 us max) + */ + timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); + timer1_write((80 / 16) * MICROS_PER_TICK); // 80 for 80 and 160! MHz clock, 16 for TIM_DIV16 above +} + +/********************************************************** + * ESP32 boards - can use any pin for send PWM + * Receive timer and send generation are independent, + * so it is recommended to always define SEND_PWM_BY_TIMER + **********************************************************/ +#elif defined(ESP32) +# if !defined(ESP_ARDUINO_VERSION) +#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 +# endif +# if !defined(ESP_ARDUINO_VERSION_VAL) +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) +# endif + +// Variables specific to the ESP32. +// the ledc functions behave like hardware timers for us :-), so we do not require our own soft PWM generation code. +hw_timer_t *s50usTimer = nullptr; // set by timerConfigForReceive() + +# if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0) && !defined(SEND_LEDC_CHANNEL) +#define SEND_LEDC_CHANNEL 0 // The channel used for PWM 0 to 7 are high speed PWM channels +# endif + +void timerEnableReceiveInterrupt() { +# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) + timerStart(s50usTimer); +# else + timerAlarmEnable(s50usTimer); +# endif +} + +# if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(2, 0, 2) +/* + * Special support for ESP core < 202 + */ +void timerDisableReceiveInterrupt() { + if (s50usTimer != nullptr) { + timerDetachInterrupt(s50usTimer); + timerEnd(s50usTimer); + } +} +# else + +void timerDisableReceiveInterrupt() { + if (s50usTimer != nullptr) { +# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) + timerStop(s50usTimer); +# else + timerAlarmDisable(s50usTimer); +# endif + } +} +# endif + +// Undefine ISR, because we register/call the plain function IRReceiveTimerInterruptHandler() +# if defined(ISR) +#undef ISR +# endif + +# if !defined(DISABLE_CODE_FOR_RECEIVER) // Otherwise the &IRReceiveTimerInterruptHandler is referenced, but not available +void timerConfigForReceive() { + // ESP32 has a proper API to setup timers, no weird chip macros needed + // simply call the readable API versions :) + // 3 timers, choose #1, 80 divider for microsecond precision @80MHz clock, count_up = true + if(s50usTimer == nullptr) { +# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) + s50usTimer = timerBegin(1000000); // Only 1 parameter is required. 1000000 corresponds to 1 MHz / 1 uSec. After successful setup the timer will automatically start. + timerStop(s50usTimer); // Stop it here, to avoid "error E (3447) gptimer: gptimer_start(348): timer is not enabled yet" at timerEnableReceiveInterrupt() + timerAttachInterrupt(s50usTimer, &IRReceiveTimerInterruptHandler); + timerAlarm(s50usTimer, MICROS_PER_TICK, true, 0); // 0 in the last parameter is repeat forever +# else + s50usTimer = timerBegin(1, 80, true); + timerAttachInterrupt(s50usTimer, &IRReceiveTimerInterruptHandler, false); // false -> level interrupt, true -> edge interrupt, but this is not supported :-( + timerAlarmWrite(s50usTimer, MICROS_PER_TICK, true); +# endif + } + // every 50 us, autoreload = true +} +# endif + + +uint8_t sLastSendPin = 0; // Avoid multiple attach() or if pin changes, detach before attach + +# if defined(SEND_PWM_BY_TIMER) +void enableSendPWMByTimer() { +# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) +# if defined(IR_SEND_PIN) + ledcWrite(IR_SEND_PIN, (IR_SEND_DUTY_CYCLE_PERCENT * 256) / 100); // 3.x API +# else + ledcWrite(IrSender.sendPin, (IR_SEND_DUTY_CYCLE_PERCENT * 256) / 100); // 3.x API +# endif +# else + // ESP version < 3.0 + ledcWrite(SEND_LEDC_CHANNEL, (IR_SEND_DUTY_CYCLE_PERCENT * 256) / 100); // * 256 since we have 8 bit resolution +# endif +} +void disableSendPWMByTimer() { +# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) +# if defined(IR_SEND_PIN) + ledcWrite(IR_SEND_PIN, 0); // 3.x API +# else + ledcWrite(IrSender.sendPin, 0); // 3.x API +# endif +# else + // ESP version < 3.0 + ledcWrite(SEND_LEDC_CHANNEL, 0); +# endif +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() (or enableHighFrequencyIROut()) + * ledcWrite since ESP 2.0.2 does not work if pin mode is set. + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { +# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) +# if defined(IR_SEND_PIN) + if(sLastSendPin == 0){ + ledcAttach(IR_SEND_PIN, aFrequencyKHz * 1000, 8); // 3.x API + sLastSendPin = IR_SEND_PIN; + } +# else + if(sLastSendPin != 0 && sLastSendPin != IrSender.sendPin){ + ledcDetach(IrSender.sendPin); // detach pin before new attaching see #1194 + } + ledcAttach(IrSender.sendPin, aFrequencyKHz * 1000, 8); // 3.x API + sLastSendPin = IrSender.sendPin; +# endif +# else + // ESP version < 3.0 + ledcSetup(SEND_LEDC_CHANNEL, aFrequencyKHz * 1000, 8); // 8 bit PWM resolution +# if defined(IR_SEND_PIN) + ledcAttachPin(IR_SEND_PIN, SEND_LEDC_CHANNEL); // attach pin to channel +# else + if(sLastSendPin != 0 && sLastSendPin != IrSender.sendPin){ + ledcDetachPin(IrSender.sendPin); // detach pin before new attaching see #1194 + } + ledcAttachPin(IrSender.sendPin, SEND_LEDC_CHANNEL); // attach pin to channel + sLastSendPin = IrSender.sendPin; +# endif +# endif +} +# endif // defined(SEND_PWM_BY_TIMER) + +/*************************************** + * SAMD boards like DUE and Zero + ***************************************/ +#elif defined(ARDUINO_ARCH_SAMD) +# if defined(SEND_PWM_BY_TIMER) +#error PWM generation by hardware is not yet implemented for SAMD +# endif + +# if !defined(IR_SAMD_TIMER) +# if defined(__SAMD51__) +# if defined(TC5) +#define IR_SAMD_TIMER TC5 +#define IR_SAMD_TIMER_IRQ TC5_IRQn +# else +#define IR_SAMD_TIMER TC3 +#define IR_SAMD_TIMER_IRQ TC3_IRQn +# endif +# else +// SAMD21 +#define IR_SAMD_TIMER TC3 +#define IR_SAMD_TIMER_ID GCLK_CLKCTRL_ID_TCC2_TC3 +#define IR_SAMD_TIMER_IRQ TC3_IRQn +# endif +# endif + +void timerEnableReceiveInterrupt() { + NVIC_EnableIRQ (IR_SAMD_TIMER_IRQ); +} +void timerDisableReceiveInterrupt() { + NVIC_DisableIRQ (IR_SAMD_TIMER_IRQ); // or TC5->INTENCLR.bit.MC0 = 1; or TC5->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; + +} +// Undefine ISR, because we call the plain function IRReceiveTimerInterruptHandler() +// The ISR is now TC3_Handler() or TC5_Handler() below +# if defined(ISR) +#undef ISR +# endif + +/** + * Adafruit M4 code (cores/arduino/startup.c) configures these clock generators: + * GCLK0 = F_CPU + * GCLK2 = 100 MHz + * GCLK1 = 48 MHz // This Clock is present in SAMD21 and SAMD51 + * GCLK4 = 12 MHz + * GCLK3 = XOSC32K + */ +void timerConfigForReceive() { + TcCount16 *TC = (TcCount16*) IR_SAMD_TIMER; + +# if defined(__SAMD51__) + // Enable the TC5 clock, use generic clock generator 0 (F_CPU) for TC5 +# if defined(TC5_GCLK_ID) + GCLK->PCHCTRL[TC5_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +# else + GCLK->PCHCTRL[TC3_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); +# endif + + // The TC should be disabled before the TC is reset in order to avoid undefined behavior. + TC->CTRLA.reg &= ~TC_CTRLA_ENABLE; // Disable the Timer + while (TC->SYNCBUSY.bit.ENABLE) + ; // Wait for disabled + // Reset TCx + TC->CTRLA.reg = TC_CTRLA_SWRST; + // When writing a '1' to the CTRLA.SWRST bit it will immediately read as '1'. + while (TC->SYNCBUSY.bit.SWRST) + ; // CTRL.SWRST will be cleared by hardware when the peripheral has been reset. + + // SAMD51 has F_CPU = 120 MHz + TC->CC[0].reg = ((MICROS_PER_TICK * (F_CPU / MICROS_IN_ONE_SECOND)) / 16) - 1; // (375 - 1); + + /* + * Set timer counter mode to 16 bits, set mode as match frequency, prescaler is DIV16 => 7.5 MHz clock, start counter + */ + TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16 | TC_WAVE_WAVEGEN_MFRQ | TC_CTRLA_PRESCALER_DIV16 | TC_CTRLA_ENABLE; +// while (TC5->COUNT16.STATUS.bit.SYNCBUSY == 1); // The next commands do an implicit wait :-) +# else + // Enable GCLK and select GCLK0 (F_CPU) as clock for TC4 and TC5 + REG_GCLK_CLKCTRL = (uint16_t)(GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | IR_SAMD_TIMER_ID); + while (GCLK->STATUS.bit.SYNCBUSY == 1) + ; + + // The TC should be disabled before the TC is reset in order to avoid undefined behavior. + TC->CTRLA.reg &= ~TC_CTRLA_ENABLE; + // When write-synchronization is ongoing for a register, any subsequent write attempts to this register will be discarded, and an error will be reported. + while (TC->STATUS.bit.SYNCBUSY == 1) + ; // wait for sync to ensure that we can write again to COUNT16.CTRLA.reg + // Reset TCx + TC->CTRLA.reg = TC_CTRLA_SWRST; + // When writing a 1 to the CTRLA.SWRST bit it will immediately read as 1. + while (TC->CTRLA.bit.SWRST) + ; // CTRL.SWRST will be cleared by hardware when the peripheral has been reset. + + // SAMD51 has F_CPU = 48 MHz + TC->CC[0].reg = ((MICROS_PER_TICK * (F_CPU / MICROS_IN_ONE_SECOND)) / 16) - 1; // (150 - 1); + + /* + * Set timer counter mode to 16 bits, set mode as match frequency, prescaler is DIV16 => 3 MHz clock, start counter + */ + TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_MFRQ | TC_CTRLA_PRESCALER_DIV16 | TC_CTRLA_ENABLE; + +# endif + // Configure interrupt request + NVIC_DisableIRQ (IR_SAMD_TIMER_IRQ); + NVIC_ClearPendingIRQ(IR_SAMD_TIMER_IRQ); + NVIC_SetPriority(IR_SAMD_TIMER_IRQ, 0); + NVIC_EnableIRQ(IR_SAMD_TIMER_IRQ); + + // Enable the compare interrupt + TC->INTENSET.bit.MC0 = 1; +} + +# if !defined(DISABLE_CODE_FOR_RECEIVER) +# if defined(__SAMD51__) && defined(TC5) +void TC5_Handler(void) +# else +void TC3_Handler(void) +# endif // defined(__SAMD51__) +{ + TcCount16 *TC = (TcCount16*) IR_SAMD_TIMER; + // Check for right interrupt bit + if (TC->INTFLAG.bit.MC0 == 1) { + // reset bit for next turn + TC->INTFLAG.bit.MC0 = 1; + IRReceiveTimerInterruptHandler(); + } +} +# endif // !defined(DISABLE_CODE_FOR_RECEIVER) + +/*************************************** + * Mbed based boards + ***************************************/ +#elif defined(ARDUINO_ARCH_MBED) // Arduino Nano 33 BLE + Sparkfun Apollo3 + Nano RP2040 Connect +#include "mbed.h" +mbed::Ticker s50usTimer; + +// Undefine ISR, because we register/call the plain function IRReceiveTimerInterruptHandler() +# if defined(ISR) +#undef ISR +# endif + +void timerEnableReceiveInterrupt() { + s50usTimer.attach(IRReceiveTimerInterruptHandler, std::chrono::microseconds(MICROS_PER_TICK)); +} +void timerDisableReceiveInterrupt() { + s50usTimer.detach(); +} + +void timerConfigForReceive() { + s50usTimer.attach(IRReceiveTimerInterruptHandler, std::chrono::microseconds(MICROS_PER_TICK)); +} + +# if defined(SEND_PWM_BY_TIMER) +#include "pins_arduino.h" // for digitalPinToPinName() + +# if defined(IR_SEND_PIN) +mbed::PwmOut sPwmOutForSendPWM(digitalPinToPinName(IR_SEND_PIN)); +# else +mbed::PwmOut sPwmOutForSendPWM(digitalPinToPinName(IrSender.sendPin)); +# endif +uint8_t sIROutPuseWidth; + +void enableSendPWMByTimer() { + sPwmOutForSendPWM.pulsewidth_us(sIROutPuseWidth); +} +//void enableSendPWMByTimer() { sPwmOutForSendPWM.resume(); sPwmOutForSendPWM.pulsewidth_us(sIROutPuseWidth);} +//void disableSendPWMByTimer() { sPwmOutForSendPWM.suspend();} // this kills pulsewidth_us value and does not set output level to LOW + +void disableSendPWMByTimer() { + sPwmOutForSendPWM.pulsewidth_us(0); // this also sets output level to LOW :-) +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { + sPwmOutForSendPWM.period_us(1000 / aFrequencyKHz); // 26.315 for 38 kHz + sIROutPuseWidth = (1000 * IR_SEND_DUTY_CYCLE_PERCENT) / (aFrequencyKHz * 100); +} +# endif // defined(SEND_PWM_BY_TIMER) + +/************************************************************************************************************************************* + * RP2040 based boards for pico core + * https://github.com/earlephilhower/arduino-pico + * https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json + * Can use any pin for PWM, no timer restrictions + *************************************************************************************************************************************/ +#elif defined(ARDUINO_ARCH_RP2040) // Raspberry Pi Pico, Adafruit Feather RP2040, etc. +#include "pico/time.h" + +repeating_timer_t s50usTimer; + +// Undefine ISR, because we register/call the plain function IRReceiveTimerInterruptHandler() +# if defined(ISR) +#undef ISR +# endif + +// The timer callback has a parameter and a return value +bool IRTimerInterruptHandlerHelper(repeating_timer_t*) { + IRReceiveTimerInterruptHandler(); + return true; +} + +void timerEnableReceiveInterrupt() { + add_repeating_timer_us(-(MICROS_PER_TICK), IRTimerInterruptHandlerHelper, nullptr, &s50usTimer); +} +void timerDisableReceiveInterrupt() { + cancel_repeating_timer(&s50usTimer); +} + +void timerConfigForReceive() { + // no need for initializing timer at setup() +} + +# if defined(SEND_PWM_BY_TIMER) +#include "hardware/pwm.h" + +uint sSliceNumberForSendPWM; +uint sChannelNumberForSendPWM; +uint sIROutPuseWidth; + +/* + * If we just disable the PWM, the counter stops and the output stays at the state is currently has + */ +void enableSendPWMByTimer() { + pwm_set_counter(sSliceNumberForSendPWM, 0); + pwm_set_chan_level(sSliceNumberForSendPWM, sChannelNumberForSendPWM, sIROutPuseWidth); +} +void disableSendPWMByTimer() { + pwm_set_chan_level(sSliceNumberForSendPWM, sChannelNumberForSendPWM, 0); // this sets output also to LOW +} + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { +# if defined(IR_SEND_PIN) + gpio_set_function(IR_SEND_PIN, GPIO_FUNC_PWM); + // Find out which PWM slice is connected to IR_SEND_PIN + sSliceNumberForSendPWM = pwm_gpio_to_slice_num(IR_SEND_PIN); + sChannelNumberForSendPWM = pwm_gpio_to_channel(IR_SEND_PIN); +# else + gpio_set_function(IrSender.sendPin, GPIO_FUNC_PWM); + // Find out which PWM slice is connected to IR_SEND_PIN + sSliceNumberForSendPWM = pwm_gpio_to_slice_num(IrSender.sendPin); + sChannelNumberForSendPWM = pwm_gpio_to_channel(IrSender.sendPin); +# endif + uint16_t tPWMWrapValue = (clock_get_hz(clk_sys)) / (aFrequencyKHz * 1000); // 3289.473 for 38 kHz @125 MHz clock. We have a 16 bit counter and use system clock (125 MHz) + + pwm_config tPWMConfig = pwm_get_default_config(); + pwm_config_set_wrap(&tPWMConfig, tPWMWrapValue - 1); + pwm_init(sSliceNumberForSendPWM, &tPWMConfig, false); // we do not want to send now + sIROutPuseWidth = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; // 985.84 for 38 kHz + pwm_set_chan_level(sSliceNumberForSendPWM, sChannelNumberForSendPWM, 0); + pwm_set_enabled(sSliceNumberForSendPWM, true); +} +# endif // defined(SEND_PWM_BY_TIMER) + +/*************************************** + * NRF5 boards like the BBC:Micro + ***************************************/ +#elif defined(NRF5) || defined(ARDUINO_ARCH_NRF52840) || defined(ARDUINO_ARCH_NRF52) +# if defined(SEND_PWM_BY_TIMER) +#error PWM generation by hardware not implemented for NRF5 +# endif + +void timerEnableReceiveInterrupt() { + NVIC_EnableIRQ (TIMER2_IRQn); +} +void timerDisableReceiveInterrupt() { + NVIC_DisableIRQ (TIMER2_IRQn); +} + +// Undefine ISR, because we call the plain function IRReceiveTimerInterruptHandler() +# if defined(ISR) +#undef ISR +# endif + +void timerConfigForReceive() { + NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer; // Set the timer in Timer Mode + NRF_TIMER2->TASKS_CLEAR = 1; // clear the task first to be usable for later + NRF_TIMER2->PRESCALER = 4; // f TIMER = 16 MHz / (2 ^ PRESCALER ) : 4 -> 1 MHz, 1 uS + NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_16Bit; //Set counter to 16 bit resolution + NRF_TIMER2->CC[0] = MICROS_PER_TICK; //Set value for TIMER2 compare register 0, to trigger every 50 uS + NRF_TIMER2->CC[1] = 0; //Set value for TIMER2 compare register 1 + + // Enable interrupt on Timer 2, for CC[0] compare match events + NRF_TIMER2->INTENSET = (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos); + NRF_TIMER2->TASKS_START = 1; // Start TIMER2 + + // timerAttachInterrupt(timer, &IRTimerInterruptHandler, 1); +} + +#if !defined(DISABLE_CODE_FOR_RECEIVER) +/** TIMTER2 peripheral interrupt handler. This interrupt handler is called whenever there it a TIMER2 interrupt + * Don't mess with this line. really. + */ +extern "C" { +void TIMER2_IRQHandler(void) { + // Interrupt Service Routine - Fires every 50uS + if ((NRF_TIMER2->EVENTS_COMPARE[0] != 0) && ((NRF_TIMER2->INTENSET & TIMER_INTENSET_COMPARE0_Msk) != 0)) { + NRF_TIMER2->EVENTS_COMPARE[0] = 0; //Clear compare register 0 event + IRReceiveTimerInterruptHandler(); // call the IR-receive function + NRF_TIMER2->CC[0] += 50; + } +} +} +#endif + +/********************************************************************************************************************** + * BluePill in 2 flavors see https://samuelpinches.com.au/3d-printer/cutting-through-some-confusion-on-stm32-and-arduino/ + * + * Recommended original Arduino_STM32 by Roger Clark. + * http://dan.drown.org/stm32duino/package_STM32duino_index.json + * STM32F1 architecture for "Generic STM32F103C series" from "STM32F1 Boards (Arduino_STM32)" of Arduino Board manager + **********************************************************************************************************************/ +#elif defined(__STM32F1__) || defined(ARDUINO_ARCH_STM32F1) +#include // 4 timers and 4. timer (4.channel) is used for tone() +# if defined(SEND_PWM_BY_TIMER) +#error PWM generation by hardware not implemented for STM32 +# endif + +/* + * Use timer 3 as IR timer. + * Timer 3 blocks PA6, PA7, PB0, PB1, so if you require one of them as tone() or Servo output, you must choose another timer. + */ +HardwareTimer s50usTimer(3); + +void timerEnableReceiveInterrupt() { + s50usTimer.resume(); +} +void timerDisableReceiveInterrupt() { + s50usTimer.pause(); +} + +// Undefine ISR, because we register/call the plain function IRReceiveTimerInterruptHandler() +# if defined(ISR) +#undef ISR +# endif + +void timerConfigForReceive() { + s50usTimer.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); + s50usTimer.setPrescaleFactor(1); + s50usTimer.setOverflow((F_CPU / MICROS_IN_ONE_SECOND) * MICROS_PER_TICK); + s50usTimer.attachInterrupt(TIMER_CH1, IRReceiveTimerInterruptHandler); + s50usTimer.refresh(); +} + +/********************************************************************************************************************** + * STM32duino by ST Microsystems. + * https://github.com/stm32duino/Arduino_Core_STM32 + * https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json + * stm32 architecture for "Generic STM32F1 series" from "STM32 Boards (selected from submenu)" of Arduino Board manager + **********************************************************************************************************************/ +#elif defined(STM32F1xx) || defined(ARDUINO_ARCH_STM32) +#include // 4 timers and 3. timer is used for tone(), 2. for Servo +# if defined(SEND_PWM_BY_TIMER) +#error PWM generation by hardware not implemented for STM32 +# endif + +/* + * Use timer 4 as IR timer. + * Timer 4 blocks PB6, PB7, PB8, PB9, so if you need one them as tone() or Servo output, you must choose another timer. + */ +# if defined(TIM4) +HardwareTimer s50usTimer(TIM4); +# else +HardwareTimer s50usTimer(TIM2); +# endif + +void timerEnableReceiveInterrupt() { + s50usTimer.resume(); +} +void timerDisableReceiveInterrupt() { + s50usTimer.pause(); +} + +// Undefine ISR, because we register/call the plain function IRReceiveTimerInterruptHandler() +# if defined(ISR) +#undef ISR +# endif + +void timerConfigForReceive() { + s50usTimer.setOverflow(MICROS_PER_TICK, MICROSEC_FORMAT); // 50 uS + s50usTimer.attachInterrupt(IRReceiveTimerInterruptHandler); + s50usTimer.resume(); +} + +/*************************************** + * Particle special IntervalTimer + * !!!UNTESTED!!! + ***************************************/ +#elif defined(PARTICLE) +# ifndef __INTERVALTIMER_H__ +#include "SparkIntervalTimer.h" // SparkIntervalTimer.h is required if PARTICLE is defined. +# endif + +extern IntervalTimer timer; +extern int ir_out_kHz; + +void timerEnableReceiveInterrupt() { + timer.begin(IRReceiveTimerInterruptHandler, MICROS_PER_TICK, uSec); +} +void timerDisableReceiveInterrupt() { + timer.end(); +} + +// Undefine ISR, because we register/call the plain function IRReceiveTimerInterruptHandler() +# if defined(ISR) +#undef ISR +# endif + +void timerConfigForReceive() { +} + +# if defined(SEND_PWM_BY_TIMER) +# if defined(IR_SEND_PIN) +void enableSendPWMByTimer() { + analogWrite(IR_SEND_PIN, ((256L * 100) / IR_SEND_DUTY_CYCLE_PERCENT)), ir_out_kHz*1000); +} +void disableSendPWMByTimer() { + analogWrite(IR_SEND_PIN, 0, ir_out_kHz*1000); +} +# else +void enableSendPWMByTimer() { + analogWrite(IrSender.sendPin, ((256L * 100) / IR_SEND_DUTY_CYCLE_PERCENT), ir_out_kHz * 1000); +} +void disableSendPWMByTimer() { + analogWrite(IrSender.sendPin, 0, ir_out_kHz * 1000); +} +# endif + + +/* + * timerConfigForSend() is used exclusively by IRsend::enableIROut() + * Set output pin mode and disable receive interrupt if it uses the same resource + */ +void timerConfigForSend(uint16_t aFrequencyKHz) { + timerDisableReceiveInterrupt(); +# if defined(IR_SEND_PIN) + pinMode(IR_SEND_PIN, OUTPUT); +# else + pinMode(IrSender.sendPin, OUTPUT); +# endif + ir_out_kHz = aFrequencyKHz; +} +# endif // defined(SEND_PWM_BY_TIMER) + +/*************************************** + * Unknown CPU board + ***************************************/ +#else +#error Internal code configuration error, no timer functions implemented for this CPU / board +/* + * Dummy definitions to avoid more irritating compile errors + */ + +void timerEnableReceiveInterrupt() {}; +void timerDisableReceiveInterrupt() {}; + +# if defined(ISR) +#undef ISR +# endif +#define ISR() void notImplemented(void) + +void timerConfigForReceive() { +} + +# if defined(SEND_PWM_BY_TIMER) +void enableSendPWMByTimer() { +} +void disableSendPWMByTimer() { +} + +void timerConfigForSend(uint16_t aFrequencyKHz) { + timerDisableReceiveInterrupt(); +# if defined(IR_SEND_PIN) + pinMode(IR_SEND_PIN, OUTPUT); +# else + pinMode(IrSender.sendPin, OUTPUT); +# endif + (void) aFrequencyKHz; +} +# endif // defined(SEND_PWM_BY_TIMER) + +#endif // defined(DOXYGEN / CPU_TYPES) + +/** @}*/ +/** @}*/ +#endif // _IR_TIMER_HPP diff --git a/src/private/IRremoteBoardDefs.h b/src/private/IRremoteBoardDefs.h deleted file mode 100644 index 797d81871..000000000 --- a/src/private/IRremoteBoardDefs.h +++ /dev/null @@ -1,1001 +0,0 @@ -/** - * @file IRremoteBoardDefs.h - * - * @brief All board specific information should be contained in this file. - * It defines a number of macros, depending on the board, as determined by - * pre-proccesor symbols. - * It was previously contained within IRremoteInt.h. - */ -// IRremote -// Version 2.0.1 June, 2015 -// Copyright 2009 Ken Shirriff -// For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html -// Modified by Paul Stoffregen to support other boards and timers -// -// Interrupt code based on NECIRrcv by Joe Knapp -// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 -// Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ -// -// JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) -// Whynter A/C ARC-110WD added by Francesco Meschia -// Sparkfun Pro Micro support by Alastair McCormack -//****************************************************************************** -#ifndef IRremoteBoardDefs_h -#define IRremoteBoardDefs_h - -#ifdef ARDUINO_ARCH_AVR -#include -#define HAS_FLASH_READ 1 -#define STRCPY_PF_CAST(x) (x) -#else -#define HAS_FLASH_READ 0 -#endif - -// Define some defaults, that some boards may like to override -// (This is to avoid negative logic, ! DONT_... is just awkward.) - -/** - * Defined if the standard enableIRIn function should be used. - * Undefine for boards supplying their own. - */ -#define USE_DEFAULT_ENABLE_IR_IN - -/** - * Define if the current board supports sending. - * Currently not used. - */ -#define SENDING_SUPPORTED - -/** - * Defined if the standard enableIROut function should be used. - * Undefine for boards supplying their own. - */ -#define USE_DEFAULT_ENABLE_IR_OUT - -/** - * Duty cycle in percent for sent signals. - */ -#if ! defined(IR_SEND_DUTY_CYCLE) -#define IR_SEND_DUTY_CYCLE 30 // 30 saves power and is compatible to the old existing code -#endif - -//------------------------------------------------------------------------------ -// This first #ifdef statement contains defines for blinking the LED, -// as well as all other board specific information, with the exception of -// timers and the sending pin (IR_SEND_PIN). - -#ifdef DOXYGEN -/** - * If defined, denotes pin number of LED that should be blinked during IR reception. - * Leave undefined to disable blinking. - */ -#define BLINKLED LED_BUILTIN - -/** - * Board dependent macro to turn BLINKLED on. - */ -#define BLINKLED_ON() digitalWrite(BLINKLED, HIGH) - -/** - * Board dependent macro to turn BLINKLED off. - */ -#define BLINKLED_OFF() digitalWrite(BLINKLED, LOW) - -#elif ! defined(ARDUINO) -// Assume that we compile a test version, to be executed on the host, not on a board. - -// Do not define anything. - -#elif defined(CORE_LED0_PIN) -#define BLINKLED CORE_LED0_PIN -#define BLINKLED_ON() (digitalWrite(CORE_LED0_PIN, HIGH)) -#define BLINKLED_OFF() (digitalWrite(CORE_LED0_PIN, LOW)) - -// Sparkfun Pro Micro is __AVR_ATmega32U4__ but has different external circuit -#elif defined(ARDUINO_AVR_PROMICRO) -// We have no built in LED -> reuse RX LED -#define BLINKLED LED_BUILTIN_RX -#define BLINKLED_ON() RXLED1 -#define BLINKLED_OFF() RXLED0 - -// Arduino Leonardo + others -#elif defined(__AVR_ATmega32U4__) -#define BLINKLED LED_BUILTIN -#define BLINKLED_ON() (PORTC |= B10000000) -#define BLINKLED_OFF() (PORTC &= B01111111) - -#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega8U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) -#define BLINKLED LED_BUILTIN -#define BLINKLED_ON() (digitalWrite(LED_BUILTIN, HIGH)) -#define BLINKLED_OFF() (digitalWrite(LED_BUILTIN, LOW)) - -// Arduino Uno, Nano etc (previously default clause) -#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) || defined(__AVR_ATmega168__) -#define BLINKLED LED_BUILTIN -#define BLINKLED_ON() (PORTB |= B00100000) -#define BLINKLED_OFF() (PORTB &= B11011111) - -#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -#define BLINKLED 13 -#define BLINKLED_ON() (PORTB |= B10000000) -#define BLINKLED_OFF() (PORTB &= B01111111) - -#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) -#define BLINKLED 0 -#define BLINKLED_ON() (PORTD |= B00000001) -#define BLINKLED_OFF() (PORTD &= B11111110) - -// Nano Every, Uno WiFi Rev2, nRF5 BBC MicroBit, Nano33_BLE -#elif defined(__AVR_ATmega4809__) || defined(NRF5) || defined(ARDUINO_ARCH_NRF52840) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) -#define BLINKLED LED_BUILTIN -#define BLINKLED_ON() (digitalWrite(BLINKLED, HIGH)) -#define BLINKLED_OFF() (digitalWrite(BLINKLED, LOW)) - -// Arduino Zero -#elif defined(ARDUINO_ARCH_SAMD) -#define BLINKLED LED_BUILTIN -#define BLINKLED_ON() (digitalWrite(LED_BUILTIN, HIGH)) -#define BLINKLED_OFF() (digitalWrite(LED_BUILTIN, LOW)) - -#define USE_SOFT_SEND_PWM -// Define to use spin wait instead of delayMicros() -//#define USE_SPIN_WAIT -// Supply own enableIRIn() -#undef USE_DEFAULT_ENABLE_IR_IN - -#elif defined(ESP32) -// No system LED on ESP32, disable blinking by NOT defining BLINKLED - -// Supply own enableIRIn() and enableIROut() -#undef USE_DEFAULT_ENABLE_IR_IN -#undef USE_DEFAULT_ENABLE_IR_OUT - -#else -#warning No blinking definition found. Check IRremoteBoardDefs.h. -#ifdef LED_BUILTIN -#define BLINKLED LED_BUILTIN -#define BLINKLED_ON() digitalWrite(BLINKLED, HIGH) -#define BLINKLED_OFF() digitalWrite(BLINKLED, LOW) -#endif -#endif - -//------------------------------------------------------------------------------ -// microseconds per clock interrupt tick -#if ! defined(MICROS_PER_TICK) -#define MICROS_PER_TICK 50 -#endif - -//------------------------------------------------------------------------------ -// Define which timer to use -// -// Uncomment the timer you wish to use on your board. -// If you are using another library which uses timer2, you have options to -// switch IRremote to use a different timer. -// - -#ifndef ARDUINO -// Assume that we compile a test version, to be executed on the host, -// not on a board. - -// Do not define any timer. - -/********************* - * ARDUINO Boards - *********************/ -// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, Nano, etc -// ATmega48, ATmega88, ATmega168, ATmega328 -#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) || defined(__AVR_ATmega168__) // old default clause -# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER2) -//#define IR_USE_TIMER1 // tx = pin 9 -#define IR_USE_TIMER2 // tx = pin 3 -# endif - -// Arduino Mega -#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER2) && !defined(IR_USE_TIMER3) && !defined(IR_USE_TIMER4) && !defined(IR_USE_TIMER5) -//#define IR_USE_TIMER1 // tx = pin 11 -#define IR_USE_TIMER2 // tx = pin 9 -//#define IR_USE_TIMER3 // tx = pin 5 -//#define IR_USE_TIMER4 // tx = pin 6 -//#define IR_USE_TIMER5 // tx = pin 46 -# endif - -// Leonardo -#elif defined(__AVR_ATmega32U4__) && ! defined(TEENSYDUINO) && ! defined(ARDUINO_AVR_PROMICRO) -# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER3) && !defined(IR_USE_TIMER4_HS) -//#define IR_USE_TIMER1 // tx = pin 9 -#define IR_USE_TIMER3 // tx = pin 5 -//#define IR_USE_TIMER4_HS // tx = pin 13 -# endif - -// ATmega8U2, ATmega16U2, ATmega32U2 -#elif defined(__AVR_ATmega8U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) -# if !defined(IR_USE_TIMER1) - #define IR_USE_TIMER1 // tx = pin C6 -#endif - -// Nano Every, Uno WiFi Rev2 -#elif defined(__AVR_ATmega4809__) -# if !defined(IR_USE_TIMER_4809_1) && !defined(IR_USE_TIMER_4809_2) -#define IR_USE_TIMER_4809_1 // tx = pin 24 -//#define IR_USE_TIMER_4809_2 // TODO tx = pin 21 -# endif - -/********************* - * Plain AVR CPU's - *********************/ -// ATmega8u2, ATmega16U2, ATmega32U2 -#elif defined(__AVR_ATmega8U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) -# if !defined(IR_USE_TIMER1) - #define IR_USE_TIMER1 // tx = pin C6 -#endif - -// Atmega8 -#elif defined(__AVR_ATmega8__) -# if !defined(IR_USE_TIMER1) -#define IR_USE_TIMER1 // tx = pin 9 -# endif - - -// ATtiny84 -#elif defined(__AVR_ATtiny84__) -# if !defined(IR_USE_TIMER1) -#define IR_USE_TIMER1 // tx = pin 6 -# endif - -//ATtiny85 -#elif defined(__AVR_ATtiny85__) -# if !defined(IR_USE_TIMER_TINY0) && !defined(IR_USE_TIMER_TINY1) -# if defined(TIMER_TO_USE_FOR_MILLIS) && (TIMER_TO_USE_FOR_MILLIS== 0) -// standard ATTinyCore settings use timer 0 for millis() and micros() -#define IR_USE_TIMER_TINY1 // tx = pin 4 -# else -#define IR_USE_TIMER_TINY0 // tx = pin 1 -//#define IR_USE_TIMER_TINY1 // tx = pin 4 -# endif -# endif - -/********************* - * SPARKFUN Boards - *********************/ -// Sparkfun Pro Micro -#elif defined(ARDUINO_AVR_PROMICRO) -# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER3) && !defined(IR_USE_TIMER4_HS) -//#define IR_USE_TIMER1 // tx = pin 9 -#define IR_USE_TIMER3 // tx = pin 5 -//#define IR_USE_TIMER4_HS // tx = pin 13 -# endif - -/********************* - * TEENSY Boards - *********************/ -// Teensy 1.0 -#elif defined(__AVR_AT90USB162__) -# if !defined(IR_USE_TIMER1) -#define IR_USE_TIMER1 // tx = pin 17 -# endif - -// Teensy 2.0 -#elif defined(__AVR_ATmega32U4__) && defined(TEENSYDUINO) -# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER3) && !defined(IR_USE_TIMER4_HS) -//#define IR_USE_TIMER1 // tx = pin 14 (Teensy 2.0 - physical pin: B5) -#define IR_USE_TIMER3 // tx = pin 9 (Teensy 2.0 - physical pin: C6) -//#define IR_USE_TIMER4_HS // tx = pin 10 (Teensy 2.0 - physical pin: C7) -# endif - -// Teensy 3.0 / Teensy 3.1 -#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) -# if !defined(IR_USE_TIMER_CMT) -#define IR_USE_TIMER_CMT // tx = pin 5 -# endif - -// Teensy-LC -#elif defined(__MKL26Z64__) -# if !defined(IR_USE_TIMER_TPM1) -#define IR_USE_TIMER_TPM1 // tx = pin 16 -# endif - -// Teensy++ 1.0 & 2.0 -#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) -# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER2) && !defined(IR_USE_TIMER3) -//#define IR_USE_TIMER1 // tx = pin 25 -#define IR_USE_TIMER2 // tx = pin 1 -//#define IR_USE_TIMER3 // tx = pin 16 -# endif - -/********************* - * CPU's with MegaCore - *********************/ -// MegaCore - ATmega64, ATmega128 -#elif defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) -# if !defined(IR_USE_TIMER1) - #define IR_USE_TIMER1 // tx = pin 13 -# endif - -/********************* - * CPU's with MajorCore - *********************/ -#elif defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) -# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER3) - #define IR_USE_TIMER1 // tx = pin 13 - //#define IR_USE_TIMER3 // tx = pin 12 - ATmega162 only -#endif - -/********************* - * CPU's with MightyCore - *********************/ -// MightyCore - ATmega1284 -#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) -# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER2) && !defined(IR_USE_TIMER3) -//#define IR_USE_TIMER1 // tx = pin 13 -#define IR_USE_TIMER2 // tx = pin 14 -//#define IR_USE_TIMER3 // tx = pin 6 -# endif - -// MightyCore - ATmega164, ATmega324, ATmega644 -#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ -|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ -|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ -|| defined(__AVR_ATmega164P__) -# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER2) -//#define IR_USE_TIMER1 // tx = pin 13 -#define IR_USE_TIMER2 // tx = pin 14 -# endif - -// MightyCore - ATmega8535, ATmega16, ATmega32 -#elif defined(__AVR_ATmega8535__) || defined(__AVR_ATmega16__) || defined(__AVR_ATmega32__) -# if !defined(IR_USE_TIMER1) -#define IR_USE_TIMER1 // tx = pin 13 -# endif - -/********************* - * OTHER CPU's - *********************/ -#elif defined(ESP32) -# if !defined(IR_USE_TIMER_ESP32) -#define IR_USE_TIMER_ESP32 -# endif - -#elif defined(ARDUINO_ARCH_SAMD) -#define TIMER_PRESCALER_DIV 64 - -#elif defined(NRF5) // nRF5 BBC MicroBit -// It uses Timer2 so you cannot use the Adafruit_Microbit display driver -// Sending not implemented -#undef SENDING_SUPPORTED - -// Supply own enbleIRIn -#undef USE_DEFAULT_ENABLE_IR_IN - -#else -// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, Nano, etc -// ATmega48, ATmega88, ATmega168, ATmega328 -#define IR_USE_TIMER1 // tx = pin 9 -#warning Board could not be identified from pre-processor symbols. By Default, TIMER1 has been selected for use with IRremote. Please extend IRremoteBoardDefs.h. -#endif - -// Provide default definitions, portable but possibly slower than necessary. -// digitalWrite is supposed to be slow. If this is an issue, define faster, -// board-dependent versions of these macros SENDPIN_ON(pin) and SENDPIN_OFF(pin). -// Portable, possibly slow, default definitions are given at the end of this file. -// If defining new versions, feel free to ignore the pin argument if it -// is not configurable on the current board. - -#ifndef SENDPIN_ON -/** Board dependent macro to turn on the pin given as argument. */ -#define SENDPIN_ON(pin) digitalWrite(pin, HIGH) -#endif - -#ifndef SENDPIN_OFF -/** - * Board dependent macro to turn off the pin given as argument. - */ -#define SENDPIN_OFF(pin) digitalWrite(pin, LOW) -#endif - -//------------------------------------------------------------------------------ -// CPU Frequency -// -#if !defined(SYSCLOCK) && defined(ARDUINO) // allow for processor specific code to define SYSCLOCK -# ifndef F_CPU -#error SYSCLOCK or F_CPU cannot be determined. Define it for your board in IRremoteBoardDefs.h. -# endif // ! F_CPU -/** - * Clock frequency to be used for timing. - */ -#define SYSCLOCK F_CPU // main Arduino clock -#endif // ! SYSCLOCK - -//------------------------------------------------------------------------------ -// Defines for Timer - -// We define static board specific functions here, but they are only used in a few files. -#pragma GCC diagnostic ignored "-Wunused-function" -//--------------------------------------------------------- -#ifdef DOXYGEN -/** - * If applicable, pin number for sending IR. Note that in most cases, this is not - * used and ignored if set. Instead, the sending pin is determined by the timer - * deployed. - */ -#define IR_SEND_PIN - -/** - * Interrupt service routine. Called as interrupt routine to collect read IR data. - */ -#define ISR - -#elif ! defined(ARDUINO) -// Assume that we compile a test version, to be executed on the host, -// not on a board. -// Do nothing. -# ifdef ISR -#undef ISR -# endif -#define ISR(f) void do_not_use__(void) -#define TIMER_RESET_INTR_PENDING - -//--------------------------------------------------------- -// Timer2 (8 bits) -// -#elif defined(IR_USE_TIMER2) - -#define TIMER_RESET_INTR_PENDING -#define TIMER_ENABLE_SEND_PWM (TCCR2A |= _BV(COM2B1)) // Clear OC2B on Compare Match -#define TIMER_DISABLE_SEND_PWM (TCCR2A &= ~(_BV(COM2B1))) // Normal port operation, OC2B disconnected. -#define TIMER_ENABLE_RECEIVE_INTR (TIMSK2 = _BV(OCIE2A)) // Output Compare Match A Interrupt Enable -#define TIMER_DISABLE_RECEIVE_INTR (TIMSK2 = 0) -#define TIMER_INTR_NAME TIMER2_COMPA_vect - -// The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A. -#pragma GCC diagnostic ignored "-Wunused-function" -/* - * timerConfigForSend() is used exclusively by IRsend::enableIROut() - */ -static void timerConfigForSend(uint16_t aFrequencyKHz) { - const uint16_t pwmval = (SYSCLOCK / 2000) / (aFrequencyKHz); // 2000 instead of 1000 because of Phase Correct PWM - TCCR2A = _BV(WGM20); // PWM, Phase Correct, Top is OCR2A - TCCR2B = _BV(WGM22) | _BV(CS20); // CS20 -> no prescaling - OCR2A = pwmval; - OCR2B = pwmval * IR_SEND_DUTY_CYCLE / 100; -} - -#define TIMER_COUNT_TOP (SYSCLOCK * MICROS_PER_TICK / 1000000) -/* - * timerConfigForReceive() is used exclusively by IRrecv::enableIRIn() - * It generates an interrupt each 50 (MICROS_PER_TICK) us. - */ -static void timerConfigForReceive() { -# if (TIMER_COUNT_TOP < 256) - TCCR2A = _BV(WGM21); - TCCR2B = _BV(CS20); - OCR2A = TIMER_COUNT_TOP; - TCNT2 = 0; -# else - TCCR2A = _BV(WGM21); - TCCR2B = _BV(CS21); - OCR2A = TIMER_COUNT_TOP / 8; - TCNT2 = 0; -# endif -} - -//----------------- -# if defined(CORE_OC2B_PIN) -#define IR_SEND_PIN CORE_OC2B_PIN // Teensy - -#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -#define IR_SEND_PIN 9 // Arduino Mega - -# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ -|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ -|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ -|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ -|| defined(__AVR_ATmega164P__) -#define IR_SEND_PIN 14 // MightyCore, MegaCore - -# else -#define IR_SEND_PIN 3 // Arduino Duemilanove, Diecimila, LilyPad, etc -# endif // defined(CORE_OC2B_PIN) - -//--------------------------------------------------------- -// Timer1 (16 bits) -// -#elif defined(IR_USE_TIMER1) - -#define TIMER_RESET_INTR_PENDING -#define TIMER_ENABLE_SEND_PWM (TCCR1A |= _BV(COM1A1)) -#define TIMER_DISABLE_SEND_PWM (TCCR1A &= ~(_BV(COM1A1))) - -//----------------- -# if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8515__) \ -|| defined(__AVR_ATmega8535__) || defined(__AVR_ATmega16__) \ -|| defined(__AVR_ATmega32__) || defined(__AVR_ATmega64__) \ -|| defined(__AVR_ATmega128__) || defined(__AVR_ATmega162__) -#define TIMER_ENABLE_RECEIVE_INTR (TIMSK |= _BV(OCIE1A)) -#define TIMER_DISABLE_RECEIVE_INTR (TIMSK &= ~_BV(OCIE1A)) -# else -#define TIMER_ENABLE_RECEIVE_INTR (TIMSK1 = _BV(OCIE1A)) -#define TIMER_DISABLE_RECEIVE_INTR (TIMSK1 = 0) -# endif - -//----------------- -#if defined(TIMER1_COMPA_vect) -#define TIMER_INTR_NAME TIMER1_COMPA_vect -#elif defined(TIM1_COMPA_vect) -#define TIMER_INTR_NAME TIM1_COMPA_vect -#endif - -static void timerConfigForSend(uint16_t aFrequencyKHz) { - const uint32_t pwmval = SYSCLOCK / 2000 / (aFrequencyKHz); // 2000 instead of 1000 because of Phase Correct PWM - TCCR1A = _BV(WGM11); // PWM, Phase Correct, Top is ICR1 - TCCR1B = _BV(WGM13) | _BV(CS10); // CS10 -> no prescaling - ICR1 = pwmval; - OCR1A = pwmval * IR_SEND_DUTY_CYCLE / 100; -} - -static void timerConfigForReceive() { - TCCR1A = 0; - TCCR1B = _BV(WGM12) | _BV(CS10); - OCR1A = SYSCLOCK * MICROS_PER_TICK / 1000000; - TCNT1 = 0; -} - -//----------------- -# if defined(CORE_OC1A_PIN) -#define IR_SEND_PIN CORE_OC1A_PIN // Teensy - -# elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -#define IR_SEND_PIN 11 // Arduino Mega - -# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ -|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ -|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ -|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ -|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ -|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ -|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ -|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ -|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) -#define IR_SEND_PIN 13 // MightyCore, MegaCore, MajorCore - -# elif defined(__AVR_ATtiny84__) -# define IR_SEND_PIN 6 - -# else -#define IR_SEND_PIN 9 // Arduino Duemilanove, Diecimila, LilyPad, Sparkfun Pro Micro, Leonardo etc. -# endif // defined(CORE_OC1A_PIN) - -//--------------------------------------------------------- -// Timer3 (16 bits) -// -#elif defined(IR_USE_TIMER3) - -#define TIMER_RESET_INTR_PENDING -#define TIMER_ENABLE_SEND_PWM (TCCR3A |= _BV(COM3A1)) -#define TIMER_DISABLE_SEND_PWM (TCCR3A &= ~(_BV(COM3A1))) -#define TIMER_ENABLE_RECEIVE_INTR (TIMSK3 = _BV(OCIE3A)) -#define TIMER_DISABLE_RECEIVE_INTR (TIMSK3 = 0) -#define TIMER_INTR_NAME TIMER3_COMPA_vect - -static void timerConfigForSend(uint16_t aFrequencyKHz) { - const uint32_t pwmval = SYSCLOCK / 2000 / (aFrequencyKHz); - TCCR3A = _BV(WGM31); - TCCR3B = _BV(WGM33) | _BV(CS30); - ICR3 = pwmval; - OCR3A = pwmval * IR_SEND_DUTY_CYCLE / 100; -} - -static void timerConfigForReceive() { - TCCR3A = 0; - TCCR3B = _BV(WGM32) | _BV(CS30); - OCR3A = SYSCLOCK * MICROS_PER_TICK / 1000000; - TCNT3 = 0; -} - -//----------------- -# if defined(CORE_OC3A_PIN) -#define IR_SEND_PIN CORE_OC3A_PIN // Teensy - -# elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) \ -|| defined(__AVR_ATmega32U4__) || defined(ARDUINO_AVR_PROMICRO) -#define IR_SEND_PIN 5 // Arduino Mega, Arduino Leonardo, Sparkfun Pro Micro - -# elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) -#define IR_SEND_PIN 6 // MightyCore, MegaCore - -# else -#error Please add OC3A pin number here -# endif - -//--------------------------------------------------------- -// Timer4 (10 bits, high speed option) -// -#elif defined(IR_USE_TIMER4_HS) - -#define TIMER_RESET_INTR_PENDING -# if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro -#define TIMER_ENABLE_SEND_PWM (TCCR4A |= _BV(COM4A0)) // Use complimentary OĢ…CĢ…4Ģ…AĢ… output on pin 5 -#define TIMER_DISABLE_SEND_PWM (TCCR4A &= ~(_BV(COM4A0))) // (Pro Micro does not map PC7 (32/ICP3/CLK0/OC4A) - // of ATmega32U4 ) -# else -#define TIMER_ENABLE_SEND_PWM (TCCR4A |= _BV(COM4A1)) -#define TIMER_DISABLE_SEND_PWM (TCCR4A &= ~(_BV(COM4A1))) -# endif -#define TIMER_ENABLE_RECEIVE_INTR (TIMSK4 = _BV(TOIE4)) -#define TIMER_DISABLE_RECEIVE_INTR (TIMSK4 = 0) -#define TIMER_INTR_NAME TIMER4_OVF_vect - -static void timerConfigForSend(uint16_t aFrequencyKHz) { - const uint32_t pwmval = SYSCLOCK / 2000 / (aFrequencyKHz); - TCCR4A = (1 << PWM4A); - TCCR4B = _BV(CS40); - TCCR4C = 0; - TCCR4D = (1 << WGM40); - TCCR4E = 0; - TC4H = pwmval >> 8; - OCR4C = pwmval; - TC4H = (pwmval * IR_SEND_DUTY_CYCLE / 100) >> 8; - OCR4A = (pwmval * IR_SEND_DUTY_CYCLE / 100) & 255; -} - -static void timerConfigForReceive() { - TCCR4A = 0; - TCCR4B = _BV(CS40); - TCCR4C = 0; - TCCR4D = 0; - TCCR4E = 0; - TC4H = (SYSCLOCK * MICROS_PER_TICK / 1000000) >> 8; - OCR4C = (SYSCLOCK * MICROS_PER_TICK / 1000000) & 255; - TC4H = 0; - TCNT4 = 0; -} - -//----------------- -# if defined(CORE_OC4A_PIN) -#define IR_SEND_PIN CORE_OC4A_PIN // Teensy -# elif defined(ARDUINO_AVR_PROMICRO) -#define IR_SEND_PIN 5 // Sparkfun Pro Micro -# elif defined(__AVR_ATmega32U4__) -#define IR_SEND_PIN 13 // Leonardo -# else -#error Please add OC4A pin number here -# endif - -//--------------------------------------------------------- -// Timer4 (16 bits) -// -#elif defined(IR_USE_TIMER4) - -#define TIMER_RESET_INTR_PENDING -#define TIMER_ENABLE_SEND_PWM (TCCR4A |= _BV(COM4A1)) -#define TIMER_DISABLE_SEND_PWM (TCCR4A &= ~(_BV(COM4A1))) -#define TIMER_ENABLE_RECEIVE_INTR (TIMSK4 = _BV(OCIE4A)) -#define TIMER_DISABLE_RECEIVE_INTR (TIMSK4 = 0) -#define TIMER_INTR_NAME TIMER4_COMPA_vect - -static void timerConfigForSend(uint16_t aFrequencyKHz) { - const uint32_t pwmval = SYSCLOCK / 2000 / (aFrequencyKHz); - TCCR4A = _BV(WGM41); - TCCR4B = _BV(WGM43) | _BV(CS40); - ICR4 = pwmval; - OCR4A = pwmval * IR_SEND_DUTY_CYCLE / 100; -} - -static void timerConfigForReceive() { - TCCR4A = 0; - TCCR4B = _BV(WGM42) | _BV(CS40); - OCR4A = SYSCLOCK * MICROS_PER_TICK / 1000000; - TCNT4 = 0; -} - -//----------------- -# if defined(CORE_OC4A_PIN) -#define IR_SEND_PIN CORE_OC4A_PIN -# elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -#define IR_SEND_PIN 6 // Arduino Mega -# else -#error Please add OC4A pin number here -# endif - -//--------------------------------------------------------- -// Timer5 (16 bits) -// -#elif defined(IR_USE_TIMER5) - -#define TIMER_RESET_INTR_PENDING -#define TIMER_ENABLE_SEND_PWM (TCCR5A |= _BV(COM5A1)) -#define TIMER_DISABLE_SEND_PWM (TCCR5A &= ~(_BV(COM5A1))) -#define TIMER_ENABLE_RECEIVE_INTR (TIMSK5 = _BV(OCIE5A)) -#define TIMER_DISABLE_RECEIVE_INTR (TIMSK5 = 0) -#define TIMER_INTR_NAME TIMER5_COMPA_vect - -static void timerConfigForSend(uint16_t aFrequencyKHz) { - const uint32_t pwmval = SYSCLOCK / 2000 / (aFrequencyKHz); - TCCR5A = _BV(WGM51); - TCCR5B = _BV(WGM53) | _BV(CS50); - ICR5 = pwmval; - OCR5A = pwmval * IR_SEND_DUTY_CYCLE / 100; -} - -static void timerConfigForReceive() { - TCCR5A = 0; - TCCR5B = _BV(WGM52) | _BV(CS50); - OCR5A = SYSCLOCK * MICROS_PER_TICK / 1000000; - TCNT5 = 0; -} - -//----------------- -# if defined(CORE_OC5A_PIN) -#define IR_SEND_PIN CORE_OC5A_PIN -# elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) -#define IR_SEND_PIN 46 // Arduino Mega -# else -#error Please add OC5A pin number here -# endif - -//--------------------------------------------------------- -// Special carrier modulator timer for Teensy 3.0 / Teensy 3.1 -// -#elif defined(IR_USE_TIMER_CMT) - -#define TIMER_RESET_INTR_PENDING ({ \ -uint8_t tmp __attribute__((unused)) = CMT_MSC; \ -CMT_CMD2 = 30; \ -}) - -#define TIMER_ENABLE_SEND_PWM do { \ -CORE_PIN5_CONFIG = PORT_PCR_MUX(2) | PORT_PCR_DSE | PORT_PCR_SRE; \ -} while(0) - -#define TIMER_DISABLE_SEND_PWM do { \ -CORE_PIN5_CONFIG = PORT_PCR_MUX(1) | PORT_PCR_DSE | PORT_PCR_SRE; \ -} while(0) - -#define TIMER_ENABLE_RECEIVE_INTR NVIC_ENABLE_IRQ(IRQ_CMT) -#define TIMER_DISABLE_RECEIVE_INTR NVIC_DISABLE_IRQ(IRQ_CMT) -#define TIMER_INTR_NAME cmt_isr - -//----------------- -# ifdef ISR -#undef ISR -# endif -#define ISR(f) void do_not_use__(void) - -//----------------- -#define CMT_PPS_DIV ((F_BUS + 7999999) / 8000000) -# if F_BUS < 8000000 -#error IRremote requires at least 8 MHz on Teensy 3.x -# endif - -static void timerConfigForSend(uint16_t aFrequencyKHz) { - SIM_SCGC4 |= SIM_SCGC4_CMT; - SIM_SOPT2 |= SIM_SOPT2_PTD7PAD; - CMT_PPS = CMT_PPS_DIV - 1; - CMT_CGH1 = ((F_BUS / CMT_PPS_DIV / 3000) + ((aFrequencyKHz) / 2)) / (aFrequencyKHz); - CMT_CGL1 = ((F_BUS / CMT_PPS_DIV / 1500) + ((aFrequencyKHz) / 2)) / (aFrequencyKHz); - CMT_CMD1 = 0; - CMT_CMD2 = 30; - CMT_CMD3 = 0; - CMT_CMD4 = 0; - CMT_OC = 0x60; - CMT_MSC = 0x01; -} - -static void timerConfigForReceive() { - SIM_SCGC4 |= SIM_SCGC4_CMT; - CMT_PPS = CMT_PPS_DIV - 1; - CMT_CGH1 = 1; - CMT_CGL1 = 1; - CMT_CMD1 = 0; - CMT_CMD2 = 30; - CMT_CMD3 = 0; - CMT_CMD4 = (F_BUS / 160000 + CMT_PPS_DIV / 2) / CMT_PPS_DIV - 31; - CMT_OC = 0; - CMT_MSC = 0x03; -} - -#define IR_SEND_PIN 5 - -// defines for TPM1 timer on Teensy-LC -#elif defined(IR_USE_TIMER_TPM1) -#define TIMER_RESET_INTR_PENDING FTM1_SC |= FTM_SC_TOF; -#define TIMER_ENABLE_SEND_PWM CORE_PIN16_CONFIG = PORT_PCR_MUX(3)|PORT_PCR_DSE|PORT_PCR_SRE -#define TIMER_DISABLE_SEND_PWM CORE_PIN16_CONFIG = PORT_PCR_MUX(1)|PORT_PCR_SRE -#define TIMER_ENABLE_RECEIVE_INTR NVIC_ENABLE_IRQ(IRQ_FTM1) -#define TIMER_DISABLE_RECEIVE_INTR NVIC_DISABLE_IRQ(IRQ_FTM1) -#define TIMER_INTR_NAME ftm1_isr -# ifdef ISR -#undef ISR -# endif -#define ISR(f) void do_not_use__(void) - -static void timerConfigForSend(uint16_t aFrequencyKHz) { - SIM_SCGC6 |= SIM_SCGC6_TPM1; - FTM1_SC = 0; - FTM1_CNT = 0; - FTM1_MOD = (F_PLL / 2000) / aFrequencyKHz - 1; - FTM1_C0V = (F_PLL / 6000) / aFrequencyKHz - 1; - FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); -} - -static void timerConfigForReceive() { - SIM_SCGC6 |= SIM_SCGC6_TPM1; - FTM1_SC = 0; - FTM1_CNT = 0; - FTM1_MOD = (F_PLL / 40000) - 1; - FTM1_C0V = 0; - FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0) | FTM_SC_TOF | FTM_SC_TOIE; -} -#define IR_SEND_PIN 16 - -// defines for timer_tiny0 (8 bits) -#elif defined(IR_USE_TIMER_TINY0) -#define TIMER_RESET_INTR_PENDING -#define TIMER_ENABLE_SEND_PWM (TCCR0A |= _BV(COM0B1)) -#define TIMER_DISABLE_SEND_PWM (TCCR0A &= ~(_BV(COM0B1))) -#define TIMER_ENABLE_RECEIVE_INTR (TIMSK |= _BV(OCIE0A)) -#define TIMER_DISABLE_RECEIVE_INTR (TIMSK &= ~(_BV(OCIE0A))) -#define TIMER_INTR_NAME TIMER0_COMPA_vect - -static void timerConfigForSend(uint16_t aFrequencyKHz) { - const uint16_t pwmval = SYSCLOCK / 2000 / (aFrequencyKHz); // 2000 instead of 1000 because of Phase Correct PWM - TCCR0A = _BV(WGM00); // PWM, Phase Correct, Top is OCR0A - TCCR0B = _BV(WGM02) | _BV(CS00); // CS00 -> no prescaling - OCR0A = pwmval; - OCR0B = pwmval * IR_SEND_DUTY_CYCLE / 100; -} - -#define TIMER_COUNT_TOP (SYSCLOCK * MICROS_PER_TICK / 1000000) -static void timerConfigForReceive() { -# if (TIMER_COUNT_TOP < 256) - TCCR0A = _BV(WGM01); // CTC, Top is OCR0A - TCCR0B = _BV(CS00); // No prescaling - OCR0A = TIMER_COUNT_TOP; - TCNT0 = 0; -# else - TCCR0A = _BV(WGM01); - TCCR0B = _BV(CS01); // prescaling by 8 - OCR0A = TIMER_COUNT_TOP / 8; - TCNT0 = 0; -# endif -} - -#define IR_SEND_PIN 1 - -// defines for timer_tiny1 (8 bits) -#elif defined(IR_USE_TIMER_TINY1) -#define TIMER_RESET_INTR_PENDING -#define TIMER_ENABLE_SEND_PWM TCNT1 = 0; GTCCR |= _BV(PWM1B) | _BV(COM1B0) // Enable pin 4 PWM output (PB4 - Arduino D4) -#define TIMER_DISABLE_SEND_PWM (GTCCR &= ~(_BV(PWM1B) | _BV(COM1B0))) -#define TIMER_ENABLE_RECEIVE_INTR (TIMSK |= _BV(OCIE1B)) -#define TIMER_DISABLE_RECEIVE_INTR (TIMSK &= ~(_BV(OCIE1B))) -#define TIMER_INTR_NAME TIMER1_COMPB_vect - -static void timerConfigForSend(uint16_t aFrequencyKHz) { -# if (((SYSCLOCK / 1000) / 38) < 256) - const uint16_t pwmval = (SYSCLOCK / 1000) / (aFrequencyKHz); // 421 @16 MHz, 26 @1 MHz and 38 kHz - TCCR1 = _BV(CTC1) | _BV(CS10); // CTC1 = 1: TOP value set to OCR1C, CS10 No Prescaling - OCR1C = pwmval; - OCR1B = pwmval * IR_SEND_DUTY_CYCLE / 100; - TCNT1 = 0; - GTCCR = _BV(PWM1B) | _BV(COM1B0); // PWM1B = 1: Enable PWM for OCR1B, COM1B0 Clear on compare match -# else - const uint16_t pwmval = ((SYSCLOCK / 2) / 1000) / (aFrequencyKHz); // 210 for 16 MHz and 38 kHz - TCCR1 = _BV(CTC1) | _BV(CS11); // CTC1 = 1: TOP value set to OCR1C, CS11 Prescaling by 2 - OCR1C = pwmval; - OCR1B = pwmval * IR_SEND_DUTY_CYCLE / 100; - TCNT1 = 0; - GTCCR = _BV(PWM1B) | _BV(COM1B0); // PWM1B = 1: Enable PWM for OCR1B, COM1B0 Clear on compare match -# endif -} - -#define TIMER_COUNT_TOP (SYSCLOCK * MICROS_PER_TICK / 1000000) -static void timerConfigForReceive() { -# if (TIMER_COUNT_TOP < 256) - TCCR1 = _BV(CTC1) | _BV(CS10); // Clear Timer/Counter on Compare Match, Top is OCR1C, No prescaling - GTCCR = 0; // normal, non-PWM mode - OCR1C = TIMER_COUNT_TOP; - TCNT1 = 0; -# else - TCCR1 = _BV(CTC1) | _BV(CS12); // Clear Timer/Counter on Compare Match, Top is OCR1C, prescaling by 8 - GTCCR = 0; // normal, non-PWM mode - OCR1C = TIMER_COUNT_TOP / 8; - TCNT1 = 0; -# endif -} - -#define IR_SEND_PIN 4 - -#elif defined(IR_USE_TIMER_4809_1) -// ATmega4809 TCB0 -#define TIMER_RESET_INTR_PENDING TCB0.INTFLAGS = TCB_CAPT_bm -#define TIMER_ENABLE_SEND_PWM (TCB0.CTRLB |= TCB_CCMPEN_bm) -#define TIMER_DISABLE_SEND_PWM (TCB0.CTRLB &= ~(TCB_CCMPEN_bm)) -#define TIMER_ENABLE_RECEIVE_INTR (TCB0.INTCTRL = TCB_CAPT_bm) -#define TIMER_DISABLE_RECEIVE_INTR (TCB0.INTCTRL &= ~(TCB_CAPT_bm)) -#define TIMER_INTR_NAME TCB0_INT_vect - -static void timerConfigForSend(uint16_t aFrequencyKHz) { - const uint32_t pwmval = (SYSCLOCK / 2000) / (aFrequencyKHz); - TCB0.CTRLB = TCB_CNTMODE_PWM8_gc; - TCB0.CCMPL = pwmval; - TCB0.CCMPH = (pwmval * IR_SEND_DUTY_CYCLE) / 100; - TCB0.CTRLA = (TCB_CLKSEL_CLKDIV2_gc) | (TCB_ENABLE_bm); -} - -static void timerConfigForReceive() { - TCB0.CTRLB = (TCB_CNTMODE_INT_gc); - TCB0.CCMP = ((SYSCLOCK * MICROS_PER_TICK) / 1000000); - TCB0.INTCTRL = TCB_CAPT_bm; - TCB0.CTRLA = (TCB_CLKSEL_CLKDIV1_gc) | (TCB_ENABLE_bm); -} - -#define IR_SEND_PIN 6 /* Nano Every, Uno WiFi Rev2 */ -//--------------------------------------------------------- -// ESP32 (ESP8266 should likely be added here too) -// - -// ESP32 has it own timer API and does not use these macros, but to avoid ifdef'ing -// them out in the common code, they are defined to no-op. This allows the code to compile -// (which it wouldn't otherwise) but irsend will not work until ESP32 specific code is written -// -// The timer code is in the esp32.cpp file -// -// An IRremote version for ESP8266 and ESP32 is available at https://github.com/crankyoldgit/IRremoteESP8266 -#elif defined(IR_USE_TIMER_ESP32) - -#if ! defined(IR_SEND_PIN) -#define IR_SEND_PIN 4 // can use any pin, no timer restrictions -#endif - -#if ! defined(LED_CHANNEL) -#define LED_CHANNEL 0 // The channel used for PWM 0 to 7 are high speed PWM channels -#endif - -#define TIMER_RESET_INTR_PENDING -#define TIMER_ENABLE_SEND_PWM ledcWrite(LED_CHANNEL, IR_SEND_DUTY_CYCLE) // we must use channel here not pin number -#define TIMER_DISABLE_SEND_PWM ledcWrite(LED_CHANNEL, 0) - -#ifdef ISR -#undef ISR -#endif -#define ISR(f) void IRAM_ATTR IRTimer() - -#elif defined(ARDUINO_ARCH_SAMD) -// use timer 3 hardcoded at this time - -#define IR_SEND_PIN 9 - -#define TIMER_RESET_INTR_PENDING -#define TIMER_ENABLE_SEND_PWM // Not presently used -#define TIMER_DISABLE_SEND_PWM -#define TIMER_ENABLE_RECEIVE_INTR NVIC_EnableIRQ(TC3_IRQn) // Not presently used -#define TIMER_DISABLE_RECEIVE_INTR NVIC_DisableIRQ(TC3_IRQn) -#define TIMER_INTR_NAME TC3_Handler // Not presently used -#pragma GCC diagnostic ignored "-Wunused-function" -static void timerConfigForSend(uint16_t aFrequencyKHz __attribute__((unused))) {} - -#ifdef ISR -#undef ISR -#endif -#define ISR(f) void IRTimer(void) - -#elif defined(NRF5) || defined(ARDUINO_ARCH_NRF52840) -// The default pin used used for sending. 3, A0 - left pad -#define IR_SEND_PIN 3 // dummy since sending not yet supported - -#define TIMER_RESET_INTR_PENDING - -#ifdef ISR -#undef ISR -#endif -#define ISR(f) void IRTimer(void) - -//--------------------------------------------------------- -// Unknown Timer -// -#else -#error Internal code configuration error, no known IR_USE_TIMER* defined -#endif - -#endif // ! IRremoteBoardDefs_h diff --git a/src/private/IRremoteInt.h b/src/private/IRremoteInt.h deleted file mode 100644 index ae0ae0f99..000000000 --- a/src/private/IRremoteInt.h +++ /dev/null @@ -1,117 +0,0 @@ -//****************************************************************************** -// IRremoteint.h -// IRremote -// Version 2.0.1 June, 2015 -// Initially coded 2009 Ken Shirriff http://www.righto.com -// -// Modified by Paul Stoffregen to support other boards and timers -// -// Interrupt code based on NECIRrcv by Joe Knapp -// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 -// Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ -// -// JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) -// Whynter A/C ARC-110WD added by Francesco Meschia -//****************************************************************************** - -#ifndef IRremoteint_h -#define IRremoteint_h - -//------------------------------------------------------------------------------ -// Include the Arduino header -// -#include - -// All board specific stuff have been moved to its own file, included here. -#include "IRremoteBoardDefs.h" - -//------------------------------------------------------------------------------ -// Information for the Interrupt Service Routine -// -#if ! defined(RAW_BUFFER_LENGTH) -#define RAW_BUFFER_LENGTH 101 ///< Maximum length of raw duration buffer. Must be odd. -#endif - -// ISR State-Machine : Receiver States -#define IR_REC_STATE_IDLE 0 -#define IR_REC_STATE_MARK 1 -#define IR_REC_STATE_SPACE 2 -#define IR_REC_STATE_STOP 3 - -/** - * This struct is used for the ISR (interrupt service routine) - * and is copied once only in state STATE_STOP, so only rcvstate needs to be volatile. - */ -struct irparams_struct { - // The fields are ordered to reduce memory over caused by struct-padding - volatile uint8_t rcvstate; ///< State Machine state - uint8_t recvpin; ///< Pin connected to IR data from detector - uint8_t blinkpin; - uint8_t blinkflag; ///< true -> enable blinking of pin on IR processing - uint16_t rawlen; ///< counter of entries in rawbuf - uint16_t timer; ///< State timer, counts 50uS ticks. - uint16_t rawbuf[RAW_BUFFER_LENGTH]; ///< raw data, first entry is the length of the gap between previous and current command - uint8_t overflow; ///< Raw buffer overflow occurred -}; - -extern struct irparams_struct irparams; - -//------------------------------------------------------------------------------ -// Defines for setting and clearing register bits -// -#ifndef cbi -#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) -#endif - -#ifndef sbi -#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) -#endif - -//------------------------------------------------------------------------------ -// Pulse parms are ((X*50)-100) for the Mark and ((X*50)+100) for the Space. -// First MARK is the one after the long gap -// Pulse parameters in uSec -// - -/** - * When received, marks tend to be too long and spaces tend to be too short. - * To compensate for this, MARK_EXCESS_MICROS is subtracted from all marks, and added to all spaces. - * If you set MARK_EXCESS to approx. 50us then the TSOP4838 works best. - * At 100us it also worked, but not as well. - * Set MARK_EXCESS to 100us and the VS1838 doesn't work at all. - */ -#define MARK_EXCESS_MICROS 100 - -/** Relative tolerance (in percent) for some comparisons on measured data. */ -#define TOLERANCE 25 - -/** Lower tolerance for comparison of measured data */ -//#define LTOL (1.0 - (TOLERANCE/100.)) -#define LTOL (100 - TOLERANCE) -/** Upper tolerance for comparison of measured data */ -//#define UTOL (1.0 + (TOLERANCE/100.)) -#define UTOL (100 + TOLERANCE) - -/** Minimum gap between IR transmissions, in microseconds */ -#define _GAP 5000 - -/** Minimum gap between IR transmissions, in MICROS_PER_TICK */ -#define GAP_TICKS (_GAP/MICROS_PER_TICK) - -//#define TICKS_LOW(us) ((int)(((us)*LTOL/MICROS_PER_TICK))) -//#define TICKS_HIGH(us) ((int)(((us)*UTOL/MICROS_PER_TICK + 1))) -#if MICROS_PER_TICK == 50 && TOLERANCE == 25 // Defaults -#define TICKS_LOW(us) ((us)/67 ) // (us) / ((MICROS_PER_TICK:50 / LTOL:75 ) * 100) -#define TICKS_HIGH(us) ((us)/40 + 1) // (us) / ((MICROS_PER_TICK:50 / UTOL:125) * 100) + 1 -#else - #define TICKS_LOW(us) ((uint16_t) ((long) (us) * LTOL / (MICROS_PER_TICK * 100) )) - #define TICKS_HIGH(us) ((uint16_t) ((long) (us) * UTOL / (MICROS_PER_TICK * 100) + 1)) -#endif - -//------------------------------------------------------------------------------ -// IR detector output is active low -// -#define MARK 0 ///< Sensor output for a mark ("flash") -#define SPACE 1 ///< Sensor output for a space ("gap") - -#endif diff --git a/src/sam.cpp b/src/sam.cpp deleted file mode 100644 index 927caf362..000000000 --- a/src/sam.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#if defined(ARDUINO_ARCH_SAMD) -// Support routines for SAM processor boards - -#include "IRremote.h" - -// "Idiot check" -#ifdef USE_DEFAULT_ENABLE_IR_IN -#error Must undef USE_DEFAULT_ENABLE_IR_IN -#endif - -//+============================================================================= -// ATSAMD Timer setup & IRQ functions -// - -// following based on setup from GitHub jdneo/timerInterrupt.ino - -static void setTimerFrequency(int frequencyHz) { - int compareValue = (SYSCLOCK / (TIMER_PRESCALER_DIV * frequencyHz)) - 1; - //Serial.println(compareValue); - TcCount16* TC = (TcCount16*) TC3; - // Make sure the count is in a proportional position to where it was - // to prevent any jitter or disconnect when changing the compare value. - TC->COUNT.reg = map(TC->COUNT.reg, 0, TC->CC[0].reg, 0, compareValue); - TC->CC[0].reg = compareValue; - //Serial.print("COUNT.reg "); - //Serial.println(TC->COUNT.reg); - //Serial.print("CC[0].reg "); - //Serial.println(TC->CC[0].reg); - while (TC->STATUS.bit.SYNCBUSY == 1) - ; -} - -static void startTimer() { - REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC2_TC3); - while (GCLK->STATUS.bit.SYNCBUSY == 1) - ; // wait for sync - - TcCount16* TC = (TcCount16*) TC3; - - // The TC should be disabled before the TC is reset in order to avoid undefined behavior. - TC->CTRLA.reg &= ~TC_CTRLA_ENABLE; - // When write-synchronization is ongoing for a register, any subsequent write attempts to this register will be discarded, and an error will be reported. - while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync - // Reset TCx - TC->CTRLA.reg = TC_CTRLA_SWRST; - // When writing a ‘1’ to the CTRLA.SWRST bit it will immediately read as ‘1’. - // CTRL.SWRST will be cleared by hardware when the peripheral has been reset. - while (TC->CTRLA.bit.SWRST) - ; - - // Use the 16-bit timer - // Use match mode so that the timer counter resets when the count matches the compare register - // Set prescaler to 64 - TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_MFRQ | TC_CTRLA_PRESCALER_DIV64 | TC_CTRLA_ENABLE; - - setTimerFrequency(1000000 / MICROS_PER_TICK); - - // Enable the compare interrupt - TC->INTENSET.reg = 0; - TC->INTENSET.bit.MC0 = 1; - - NVIC_EnableIRQ (TC3_IRQn); - -} - -//+============================================================================= -// initialization -// - -void IRrecv::enableIRIn() { - // Interrupt Service Routine - Fires every 50uS - //Serial.println("Starting timer"); - startTimer(); - //Serial.println("Started timer"); - - // Initialize state machine variables - irparams.rcvstate = IR_REC_STATE_IDLE; - irparams.rawlen = 0; - - // Set pin modes - pinMode(irparams.recvpin, INPUT); -} - -void IRrecv::disableIRIn() { - TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; -} - -void IRTimer(); // Defined in IRRemote as ISR(TIMER_INTR_NAME) - -void TC3_Handler(void) { - TcCount16* TC = (TcCount16*) TC3; - // If this interrupt is due to the compare register matching the timer count - // we toggle the LED. - if (TC->INTFLAG.bit.MC0 == 1) { - TC->INTFLAG.bit.MC0 = 1; - IRTimer(); - } -} - -#endif // defined(ARDUINO_ARCH_SAMD)