├── src ├── boards │ ├── Esp32.cpp │ ├── avr.h │ ├── NoBoard.h │ ├── Sam.h │ ├── Esp32.h │ ├── ATmega4809.h │ ├── Teensy3x.h │ ├── ATmega32U4.h │ ├── Due.h │ └── ATmega328P.h ├── SIL.cpp ├── version.h ├── IrReceiver.cpp ├── IrSenderSimulator.cpp ├── IrSenderSimulator.h ├── MultiDecoder.cpp ├── IrSequenceReader.h ├── Board.cpp ├── IrSenderPwmSpinWait.cpp ├── IrSenderPwmSpinWait.h ├── IrReader.cpp ├── IrSenderPwmSoftDelay.h ├── IrSenderNonMod.cpp ├── InfraredTypes.h ├── IrSenderPwmSoftDelay.cpp ├── MultiDecoder.h ├── HashDecoder.cpp ├── IrSenderPwmSoft.h ├── Nec1Renderer.cpp ├── IrSenderNonMod.h ├── Nec1Renderer.h ├── Rc5Decoder.cpp ├── IrSenderPwm.cpp ├── IrReceiver.h ├── Rc5Renderer.h ├── PinModeStatus.h ├── IrDecoder.h ├── IrSenderPwmHard.cpp ├── IrSenderPwm.h ├── IrReceiverPoll.h ├── Rc5Decoder.h ├── IrWidgetAggregating.h ├── IrSequence.cpp ├── Rc5Renderer.cpp ├── IrReceiverPoll.cpp ├── Nec1Decoder.h ├── IrSenderPwmHard.h ├── IrSenderPwmSoft.cpp ├── IrSender.cpp ├── IrSender.h ├── HashDecoder.h ├── IrSequence.h ├── Nec1Decoder.cpp ├── IrSignal.cpp ├── IrWidget.cpp ├── Arduino.h ├── IrReceiverSampler.h ├── IrReceiverSampler.cpp ├── IrReader.h ├── IrWidgetAggregating.cpp ├── IrSignal.h ├── Pronto.cpp ├── Pronto.h ├── IrWidget.h └── Board.h ├── .gitignore ├── tools └── update-gh-pages.sh ├── examples ├── Rc5Renderer │ └── Rc5Renderer.ino ├── Nec1Renderer │ └── Nec1Renderer.ino ├── IrWidgetAggregating │ └── IrWidgetAggregating.ino ├── MultiDecoder │ └── MultiDecoder.ino ├── Nec1Decoder │ └── Nec1Decoder.ino ├── IrSenderNonModInvert │ └── IrSenderNonModInvert.ino ├── ProntoReadDump │ └── ProntoReadDump.ino ├── OneButtonRemote │ └── OneButtonRemote.ino ├── IrReceiverPoll │ └── IrReceiverPoll.ino ├── IrSenderPwmSoftDelay │ └── IrSenderPwmSoftDelay.ino ├── IrSenderPwmSpinWait │ └── IrSenderPwmSpinWait.ino ├── Pronto │ └── Pronto.ino ├── IrReceiverSampler │ └── IrReceiverSampler.ino ├── IrSenderNonMod │ └── IrSenderNonMod.ino ├── allreaders │ └── allreaders.ino ├── IrSignal │ └── IrSignal.ino ├── IrSenderPwm │ └── IrSenderPwm.ino ├── IrReceiverSampler_SenderPwm │ └── IrReceiverSampler_SenderPwm.ino └── LearningHashDecoder │ └── LearningHashDecoder.ino ├── library.properties └── Makefile /src/boards/Esp32.cpp: -------------------------------------------------------------------------------- 1 | #ifdef ESP32 2 | 3 | #include "Board.h" 4 | 5 | hw_timer_t* Esp32::timer = nullptr; 6 | 7 | #endif // ESP32 8 | -------------------------------------------------------------------------------- /src/SIL.cpp: -------------------------------------------------------------------------------- 1 | #ifndef ARDUINO 2 | #include "Arduino.h" 3 | 4 | uint8_t currentWritePin = 255; 5 | struct timeval simulatedTime = getTimeOfDay(); 6 | 7 | #endif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nbproject 2 | html 3 | *~ 4 | *.o 5 | *.a 6 | test1 7 | api-doc 8 | junk 9 | xml 10 | gh-pages 11 | attic 12 | basement 13 | garbage 14 | *.gnumeric 15 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | // This file was automatically generated from library.properties; do not edit. 2 | #pragma once 3 | /** 4 | * Version of the current library. 5 | * Taken from the version in library.properties. 6 | */ 7 | #define VERSION "1.2.3" 8 | -------------------------------------------------------------------------------- /tools/update-gh-pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | doxygen 6 | 7 | WORKDIR=gh-pages 8 | ORIGINURL=`git remote get-url origin` 9 | 10 | rm -rf ${WORKDIR} 11 | git clone --depth 1 -b gh-pages ${ORIGINURL} ${WORKDIR} 12 | cd ${WORKDIR} 13 | #rm -f * 14 | cp -rf ../api-doc/* . 15 | git add * 16 | git commit -a -m "Update of API documentation" 17 | echo Now perform \"git push\" from ${WORKDIR} 18 | -------------------------------------------------------------------------------- /src/IrReceiver.cpp: -------------------------------------------------------------------------------- 1 | #include "IrReceiver.h" 2 | 3 | IrReceiver::IrReceiver(size_t bufSize, pin_t pin_, bool pullup, microseconds_t me) : IrReader(bufSize) { 4 | pin = pin_; 5 | markExcess = me; 6 | Board::getInstance()->setPinMode(pin, pullup ? INPUT_PULLUP : INPUT); 7 | } 8 | 9 | void IrReceiver::receive() { 10 | enable(); 11 | while (!isReady()) 12 | ; 13 | disable(); 14 | } 15 | -------------------------------------------------------------------------------- /src/IrSenderSimulator.cpp: -------------------------------------------------------------------------------- 1 | #include "IrSenderSimulator.h" 2 | 3 | void IrSenderSimulator::send(const IrSequence& irSequence, frequency_t frequency, dutycycle_t dutyCycle) { 4 | if (! irSequence) 5 | return; 6 | 7 | stream.print(F("IrSenderSimulator: ")); 8 | bool printedSomething = IrSignal::dumpFrequency(stream, frequency); 9 | if (printedSomething) 10 | stream.print(' '); 11 | printedSomething = IrSignal::dumpDutyCycle(stream, dutyCycle); 12 | if (printedSomething) 13 | stream.print(' '); 14 | irSequence.dump(stream, true); 15 | } 16 | -------------------------------------------------------------------------------- /examples/Rc5Renderer/Rc5Renderer.ino: -------------------------------------------------------------------------------- 1 | // Sends Rc5 0/1 twice with different toggle values. 2 | // Turns e.g. a Philips TV to channel 11. 3 | 4 | #include 5 | #include 6 | 7 | IrSender *sender; 8 | const IrSignal *signal_0; 9 | const IrSignal *signal_1; 10 | 11 | void setup() { 12 | sender = IrSenderPwm::getInstance(true); 13 | signal_0 = Rc5Renderer::newIrSignal(0, 1); // toggle = 0 14 | signal_1 = Rc5Renderer::newIrSignal(0, 1); // toggle = 1 15 | } 16 | 17 | void loop() { 18 | sender->sendIrSignal(*signal_0); 19 | delay(1000); 20 | sender->sendIrSignal(*signal_1); 21 | delay(10000); 22 | } 23 | -------------------------------------------------------------------------------- /examples/Nec1Renderer/Nec1Renderer.ino: -------------------------------------------------------------------------------- 1 | // Send nec1 122/27 (volume down for Yamaha) followed by 10 repeats 2 | 3 | #include 4 | #include 5 | 6 | const IrSignal *irSignal; 7 | 8 | void setup() { 9 | Serial.begin(115200); 10 | irSignal = Nec1Renderer::newIrSignal(122, 27); // volume_down for Yahama receivers 11 | } 12 | 13 | void loop() { 14 | // Print a textual representation to Serial. 15 | irSignal->dump(Serial, true); 16 | 17 | // Send it 11 times (payload followed by 10 repeats). 18 | IrSenderPwm::getInstance(true)->sendIrSignal(*irSignal, 11); 19 | 20 | // Wait 10 seconds. 21 | delay(10000); 22 | } 23 | -------------------------------------------------------------------------------- /examples/IrWidgetAggregating/IrWidgetAggregating.ino: -------------------------------------------------------------------------------- 1 | // This sketch demonstrates the IrWidgetAggregating. 2 | // It requires a non-demodulating sensor connected to the capture pin. 3 | 4 | #include 5 | #include 6 | 7 | static constexpr size_t BUFFERSIZE = 200U; 8 | static constexpr uint32_t BAUD = 115200U; 9 | 10 | IrWidgetAggregating *capturer; 11 | 12 | void setup() { 13 | Serial.begin(BAUD); 14 | capturer = IrWidgetAggregating::newIrWidgetAggregating(BUFFERSIZE); 15 | } 16 | 17 | void loop() { 18 | capturer->capture(); 19 | if (capturer->isEmpty()) 20 | Serial.println(F("timeout")); 21 | else { 22 | capturer->dump(Serial); 23 | Serial.println(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/MultiDecoder/MultiDecoder.ino: -------------------------------------------------------------------------------- 1 | // This sketch demonstrates the MultiDecoder. 2 | // It requires a demodulating sensor connected to pin RECEIVE_PIN. 3 | 4 | #include 5 | #include 6 | 7 | static constexpr pin_t RECEIVE_PIN = 5U; 8 | static constexpr size_t BUFFERSIZE = 200U; 9 | static constexpr uint32_t BAUD = 115200UL; 10 | 11 | IrReceiver *receiver; 12 | 13 | void setup() { 14 | Serial.begin(BAUD); 15 | receiver = IrReceiverSampler::newIrReceiverSampler(BUFFERSIZE, RECEIVE_PIN); 16 | } 17 | 18 | void loop() { 19 | receiver->receive(); 20 | 21 | if (receiver->isEmpty()) 22 | Serial.println(F("timeout")); 23 | else { 24 | MultiDecoder decoder(*receiver); 25 | if (decoder) 26 | decoder.printDecode(Serial); 27 | else 28 | Serial.println(F("No decode")); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/IrSenderSimulator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IrSender.h" 4 | 5 | /** 6 | * Simulates sending in the sense that it prints the IrSequences on the Stream 7 | * given as argument. Intended as debugging and development tool. 8 | */ 9 | class IrSenderSimulator : public IrSender { 10 | private: 11 | Stream& stream; 12 | 13 | public: 14 | IrSenderSimulator(Stream& stream_) : IrSender(Board::NO_PIN), stream(stream_) {}; 15 | IrSenderSimulator(const IrSenderSimulator& orig) : IrSender(Board::NO_PIN),stream(orig.stream) {}; 16 | virtual ~IrSenderSimulator() {}; 17 | void send(const IrSequence& irSequence, frequency_t frequency = IrSignal::defaultFrequency, dutycycle_t dutyCycle = Board::defaultDutyCycle); 18 | void enable(frequency_t, dutycycle_t d __attribute__((unused)) = Board::defaultDutyCycle ) {}; 19 | void sendSpace(microseconds_t) {}; 20 | void sendMark(microseconds_t) {}; 21 | }; 22 | -------------------------------------------------------------------------------- /examples/Nec1Decoder/Nec1Decoder.ino: -------------------------------------------------------------------------------- 1 | // This sketch uses the IrReceiveSampler to receive a signal, and tries to 2 | // decode it as a NEC signal 3 | 4 | #include 5 | #include 6 | 7 | static constexpr pin_t RECEIVE_PIN = 5U; 8 | static constexpr size_t BUFFERSIZE = 200U; 9 | static constexpr uint32_t BAUD = 115200UL; 10 | 11 | static IrReceiver *receiver; 12 | 13 | void setup() { 14 | Serial.begin(BAUD); 15 | while (!Serial) 16 | ; 17 | receiver = IrReceiverSampler::newIrReceiverSampler(BUFFERSIZE, RECEIVE_PIN); 18 | } 19 | 20 | void loop() { 21 | receiver->receive(); 22 | 23 | if (receiver->isEmpty()) 24 | Serial.println(F("timeout")); 25 | else { 26 | Nec1Decoder decoder(*receiver); 27 | if (decoder) 28 | decoder.printDecode(Serial); 29 | else 30 | Serial.println(F("No decode as NEC")); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/IrSenderNonModInvert/IrSenderNonModInvert.ino: -------------------------------------------------------------------------------- 1 | // This sketch demonstrates the usage of the non-modulating sender in inverting 2 | // mode. It can thus be used to emulate an inverting IR demodulator, like TSOP-*. 3 | 4 | #include 5 | 6 | static constexpr pin_t NON_MOD_PIN = 9U; 7 | 8 | static const microseconds_t data[] = { 9 | 9041,4507,573,573,573,573,573,1694,573,1694,573,573,573,573,573,573,573, 10 | 573,573,573,573,1694,573,573,573,573,573,573,573,1694,573,573,573,573,573, 11 | 573,573,573,573,573,573,1694,573,1694,573,1694,573,573,573,573,573,1694, 12 | 573,1694,573,1694,573,573,573,573,573,573,573,1694,573,1694,573,44293 13 | }; 14 | static const IrSequence seq(data, sizeof(data)/sizeof(microseconds_t)); 15 | 16 | static IrSenderNonMod sender(NON_MOD_PIN, true); 17 | 18 | void setup() { 19 | } 20 | 21 | void loop() { 22 | sender.sendNonModulated(seq); 23 | delay(2000); 24 | } 25 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Infrared 2 | version=1.2.3 3 | author=Bengt Martensson 4 | maintainer=Bengt Martensson 5 | sentence=An object oriented library for sending, receiving, generating, and decoding IR signals on the Arduino. 6 | paragraph=The library contains classes for IR signals, IR sequences, as well as for sending, receiving, decoding, and rendering of IR signals. 7 | category=Signal Input/Output 8 | url=http://www.harctoolbox.org/Infrared4Arduino.html 9 | architectures=avr,megaavr,samd,sam,esp32,* 10 | includes=HashDecoder.h, InfraredTypes.h, IrDecoder.h, IrReader.h, IrReceiver.h, IrReceiverPoll.h, IrReceiverSampler.h, IrSender.h, IrSenderNonMod.h, IrSenderPwm.h, IrSenderPwmHard.h, IrSenderPwmSoft.h, IrSenderPwmSoftDelay.h, IrSenderPwmSpinWait.h, IrSenderSimulator.h, IrSequence.h, IrSequenceReader.h, IrSignal.h, IrWidget.h, IrWidgetAggregating.h, MultiDecoder.h, Nec1Decoder.h, Nec1Renderer.h, Pronto.h, Rc5Decoder.h, Rc5Renderer.h 11 | -------------------------------------------------------------------------------- /src/boards/avr.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #ifndef ARDUINO_ARCH_AVR 21 | #error This file is for AVR only 22 | #endif 23 | 24 | #define STRCPY_PF_CAST(x) (x) 25 | 26 | #define HAS_FLASH_READ 1 27 | #define HAS_HARDWARE_PWM 1 28 | #define HAS_SAMPLING 1 29 | #define HAS_INPUT_CAPTURE 1 30 | -------------------------------------------------------------------------------- /src/MultiDecoder.cpp: -------------------------------------------------------------------------------- 1 | #include "MultiDecoder.h" 2 | #include "Nec1Decoder.h" 3 | #include "Rc5Decoder.h" 4 | #include 5 | 6 | MultiDecoder::MultiDecoder(const IrReader &irReader) { 7 | if (! irReader) { 8 | type = timeout; 9 | strcpy(decode, "."); 10 | return; 11 | } 12 | 13 | if (irReader.getDataLength() < 3) { 14 | type = noise; 15 | strcpy(decode, ":"); 16 | return; 17 | } 18 | 19 | Nec1Decoder nec1decoder(irReader); 20 | if (nec1decoder.isValid()) { 21 | strcpy(decode, nec1decoder.getDecode()); 22 | type = nec1decoder.isDitto() ? nec_ditto : nec; 23 | setValid(true); 24 | return; 25 | } 26 | 27 | Rc5Decoder rc5decoder(irReader); 28 | if (rc5decoder.isValid()) { 29 | strcpy(decode, rc5decoder.getDecode()); 30 | type = rc5; 31 | setValid(true); 32 | return; 33 | } 34 | 35 | // Giving up 36 | strcpy(decode, "***"); 37 | type = undecoded; 38 | } 39 | -------------------------------------------------------------------------------- /examples/ProntoReadDump/ProntoReadDump.ino: -------------------------------------------------------------------------------- 1 | // This sketch demonstrates the IrReceiverSampler together with Pronto::toHexString. 2 | // It requires a demodulating sensor connected to pin RECEIVE_PIN. 3 | // For this reason, we "guess" the modulation frequency to be 4 | // IrSignal::defaultFrequency. 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | static constexpr pin_t RECEIVE_PIN = 5U; 11 | static constexpr size_t BUFFERSIZE = 200U; 12 | static constexpr uint32_t BAUD = 115200UL; 13 | 14 | IrReceiver *receiver; 15 | 16 | void setup() { 17 | Serial.begin(BAUD); 18 | receiver = IrReceiverSampler::newIrReceiverSampler(BUFFERSIZE, RECEIVE_PIN); 19 | receiver->setEndingTimeout(100); 20 | } 21 | 22 | void loop() { 23 | receiver->receive(); // combines enable, loop, disable 24 | 25 | if (receiver->isEmpty()) 26 | Serial.println(F("timeout")); 27 | else { 28 | //receiver->dump(Serial); 29 | IrSequence* irSequence = receiver->toIrSequence(); 30 | const char* hex = Pronto::toProntoHex(*irSequence); 31 | Serial.println(hex); 32 | delete[] hex; 33 | delete irSequence; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/OneButtonRemote/OneButtonRemote.ino: -------------------------------------------------------------------------------- 1 | // Requires a switch connected between the digital input pin "button" and ground. 2 | // Sends a Yamaha volume down (NEC1 D=122, F=27) as long as the button is held 3 | // down. Due to the nature of NEC1, first the payload is sent once, then the simple 4 | // repeat signal (many times). 5 | 6 | // Button de-bouncing is not really necessary. 7 | 8 | #include 9 | #include 10 | 11 | // where the switch is connected 12 | static constexpr pin_t button = 4U; 13 | 14 | static const IrSignal *irSignal; 15 | static const IrSender *irSender; 16 | 17 | static bool buttonIsPressed() { 18 | return digitalRead(button) == LOW; 19 | } 20 | 21 | void setup() { 22 | // Define switch as input, using pullup to Vcc 23 | pinMode(button, INPUT_PULLUP); 24 | 25 | // Set up the sender 26 | irSender = IrSenderPwm::getInstance(true); 27 | 28 | // Set up the signal 29 | irSignal = Nec1Renderer::newIrSignal(122, 27); // volume_down for Yamaha receivers 30 | } 31 | 32 | void loop() { 33 | // Send the signal when and while button is pressed, 34 | // according to IrSender::sendWhile. 35 | irSender->sendWhile(*irSignal, buttonIsPressed); 36 | } 37 | -------------------------------------------------------------------------------- /src/IrSequenceReader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IrSignal.h" 4 | #include "IrReader.h" 5 | 6 | /** 7 | * This class packs an IrSequence into a dummy, immutable IrReader. 8 | * It is basically intended for debugging and such. 9 | */ 10 | class IrSequenceReader : public IrReader { 11 | private: 12 | IrSequence irSequence; 13 | 14 | public: 15 | IrSequenceReader() : irSequence() { 16 | }; 17 | 18 | IrSequenceReader(const IrSequenceReader& orig) : IrReader(),irSequence(orig.irSequence) { 19 | }; 20 | 21 | IrSequenceReader(IrSequenceReader&& orig) : IrReader(),irSequence(orig.irSequence) { 22 | }; 23 | 24 | IrSequenceReader(const IrSequence& irSequence_) : IrReader(),irSequence(irSequence_) { 25 | }; 26 | 27 | virtual ~IrSequenceReader() { 28 | }; 29 | 30 | virtual frequency_t getFrequency() const { 31 | return IrSignal::defaultFrequency; 32 | }; 33 | 34 | void receive() { 35 | }; 36 | 37 | bool isReady() const { 38 | return true; 39 | }; 40 | 41 | size_t getDataLength() const { 42 | return irSequence.getLength(); 43 | }; 44 | 45 | microseconds_t getDuration(unsigned int index) const { 46 | return irSequence[index]; 47 | }; 48 | }; 49 | -------------------------------------------------------------------------------- /examples/IrReceiverPoll/IrReceiverPoll.ino: -------------------------------------------------------------------------------- 1 | // This sketch demonstrates the IrReceiverPoll. 2 | // It requires a demodulating sensor connected to pin RECEIVE_PIN. 3 | 4 | // This sketch runs on *anything* (at least with sufficient speed and memory) 5 | // without any requirements on timers or interrupts. 6 | // However ESP8266 seen to make problems... 7 | 8 | #include 9 | #include 10 | 11 | #ifdef ESP32 12 | static constexpr pin_t RECEIVE_PIN = 4U; 13 | #elif ESP8266 14 | static constexpr pin_t RECEIVE_PIN = 2U; 15 | #else 16 | static constexpr pin_t RECEIVE_PIN = 5U; 17 | #endif 18 | 19 | static constexpr size_t BUFFERSIZE = 200UL; 20 | static constexpr uint32_t BAUD = 115200UL; 21 | 22 | IrReceiver *receiver; 23 | 24 | void setup() { 25 | Serial.begin(BAUD); 26 | while (!Serial) 27 | ; 28 | receiver = new IrReceiverPoll(BUFFERSIZE, RECEIVE_PIN); 29 | Serial.print(F("Listening on pin ")); 30 | Serial.println(receiver->getPin(), DEC); 31 | } 32 | 33 | void loop() { 34 | receiver->receive(); // combines enable, loop, disable 35 | 36 | if (receiver->isEmpty()) 37 | Serial.println(F("timeout")); 38 | else { 39 | receiver->dump(Serial); 40 | Serial.println(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Board.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #include "Board.h" 19 | 20 | /** 21 | * Version of delayMicroseconds able to grok more than 16383 micros. 22 | * @param t delay time in microseconds_t 23 | */ 24 | // See https://www.arduino.cc/reference/en/language/functions/time/delaymicroseconds/ 25 | void Board::delayMicroseconds(microseconds_t t) { 26 | if (t) { 27 | if (t > 16383U) { 28 | ::delayMicroseconds(t % 1000U); 29 | delay(t / 1000U); 30 | } else 31 | ::delayMicroseconds(t); 32 | } 33 | } 34 | 35 | Board* Board::instance = new CURRENT_CLASS(); 36 | -------------------------------------------------------------------------------- /src/IrSenderPwmSpinWait.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #include 19 | #include "IrSenderPwmSpinWait.h" 20 | 21 | IrSenderPwmSpinWait::IrSenderPwmSpinWait(pin_t sendPin) : IrSenderPwmSoft(sendPin) { 22 | } 23 | 24 | void inline IrSenderPwmSpinWait::sleepMicros(microseconds_t us) { 25 | sleepUntilMicros(micros() + us); 26 | } 27 | 28 | void inline IrSenderPwmSpinWait::sleepUntilMicros(uint32_t targetTime) { 29 | while (micros() < targetTime) { 30 | #if ! defined(ARDUINO) && ! defined(REAL_TIME) 31 | // increment the simulated time, otherwise will loop forever 32 | delayMicroseconds(1); 33 | #endif 34 | yield(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/IrSenderPwmSpinWait.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2019 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include "IrSenderPwmSoft.h" 22 | 23 | /** 24 | * @class IrSenderPwmSpinWait 25 | * 26 | * This sender class generates the modulation in software, using spin wait. 27 | * It does not use any timer, not even for delay(). 28 | * It will therefore run on all sufficiently powerful hardware. 29 | */ 30 | class IrSenderPwmSpinWait : public IrSenderPwmSoft { 31 | public: 32 | IrSenderPwmSpinWait(pin_t sendPin); 33 | 34 | virtual ~IrSenderPwmSpinWait() { 35 | }; 36 | 37 | private: 38 | void sleepMicros(microseconds_t t); 39 | void sleepUntilMicros(uint32_t t); 40 | }; 41 | -------------------------------------------------------------------------------- /src/IrReader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #include "IrReader.h" 19 | 20 | // Cannot use IrSequence.dump directly! 21 | void IrReader::dump(Stream &stream) const { 22 | size_t count = getDataLength(); 23 | for (unsigned int i = 0U; i < count; i++) { 24 | if (i > 0U) 25 | stream.print(" "); 26 | stream.print((i & 1U) ? '-' : '+'); 27 | stream.print(getDuration(i), DEC); 28 | } 29 | } 30 | 31 | IrSequence *IrReader::toIrSequence() const { 32 | microseconds_t *durations = new microseconds_t[getDataLength()]; 33 | for (unsigned int i = 0; i < getDataLength(); i++) 34 | durations[i] = getDuration(i); 35 | return new IrSequence(durations, getDataLength()); 36 | } 37 | -------------------------------------------------------------------------------- /src/IrSenderPwmSoftDelay.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include "IrSenderPwmSoft.h" 22 | 23 | /** 24 | * Sending function using timer PWM. Due to the nature of the timers, this is a Highlander, 25 | * ("There can only be one"), so the class is a singleton class, with private constructor, 26 | * a factory method that enforces the "highlander property". 27 | */ 28 | class IrSenderPwmSoftDelay : public IrSenderPwmSoft { 29 | public: 30 | IrSenderPwmSoftDelay(pin_t outpitPin); // default is not meaningful!! 31 | 32 | virtual ~IrSenderPwmSoftDelay() { 33 | } 34 | 35 | private: 36 | void sleepMicros(microseconds_t us); 37 | void sleepUntilMicros(uint32_t terminateTime); 38 | }; 39 | -------------------------------------------------------------------------------- /examples/IrSenderPwmSoftDelay/IrSenderPwmSoftDelay.ino: -------------------------------------------------------------------------------- 1 | // This sketch sends a raw signal using the PWM sender every 5 seconds. 2 | // It requires an IR-Led connected to the sending pin 3 | 4 | #include 5 | 6 | static constexpr frequency_t necFrequency = 38400U; 7 | static constexpr pin_t pin = 8 | #ifdef ESP8266 9 | 4U; // D2 on ESP8266 10 | #else 11 | 3U; 12 | #endif 13 | static constexpr uint32_t BAUD = 115200UL; 14 | 15 | // NEC(1) 122 29 with no repetition; powers on many Yamaha receivers 16 | static const microseconds_t array[] = { 17 | 9024, 4512, 564, 564, 564, 1692, 564, 564, 564, 1692, 564, 1692, 18 | 564, 1692, 564, 1692, 564, 564, 564, 1692, 564, 564, 564, 1692, 19 | 564, 564, 564, 564, 564, 564, 564, 564, 564, 1692, 564, 1692, 564, 20 | 564, 564, 1692, 564, 1692, 564, 1692, 564, 564, 564, 564, 564, 564, 21 | 564, 564, 564, 1692, 564, 564, 564, 564, 564, 564, 564, 1692, 564, 22 | 1692, 564, 1692, 564, 39756 23 | }; 24 | 25 | static const IrSequence irSequence(array, sizeof(array) / sizeof(microseconds_t)); 26 | static IrSender* irSender; 27 | 28 | void setup() { 29 | Serial.begin(BAUD); 30 | while (!Serial) 31 | ; 32 | irSender = new IrSenderPwmSoftDelay(pin); 33 | } 34 | 35 | void loop() { 36 | Serial.print(F("Sending @ pin ")); 37 | Serial.println(pin, DEC); 38 | irSender->send(irSequence, necFrequency); 39 | delay(5000); 40 | } 41 | -------------------------------------------------------------------------------- /src/IrSenderNonMod.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #include "IrSenderNonMod.h" 19 | 20 | IrSenderNonMod::IrSenderNonMod(pin_t pin, bool _invert) : IrSender(pin),invert(_invert) { 21 | } 22 | 23 | void IrSenderNonMod::sendNonModulated(const IrSequence& irSequence, unsigned int times) { 24 | for (unsigned int i = 0; i < times; i++) 25 | send(irSequence); 26 | } 27 | 28 | void IrSenderNonMod::sendSpace(microseconds_t time) { 29 | if (invert) 30 | writeHigh(); 31 | else 32 | writeLow(); 33 | Board::delayMicroseconds(time); 34 | } 35 | 36 | void IrSenderNonMod::sendMark(microseconds_t time) { 37 | if (invert) 38 | writeLow(); 39 | else 40 | writeHigh(); 41 | Board::delayMicroseconds(time); 42 | } 43 | -------------------------------------------------------------------------------- /src/InfraredTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /** 6 | * @file InfraredTypes.h 7 | * @brief This file defines some general data types that are used in the library. 8 | */ 9 | 10 | /** 11 | * Type for durations in micro seconds. Change to a longer type if needed, 12 | * AND you know what you are doing. 13 | * But DO NOT use a system dependent type like int! 14 | */ 15 | typedef uint16_t microseconds_t; 16 | //typedef uint32_t microseconds_t; 17 | /** Largest microseconds_t number possible */ 18 | static constexpr microseconds_t MICROSECONDS_T_MAX = static_cast(-1); 19 | 20 | /** 21 | * Type for durations in milli seconds. 22 | * Using a larger type than 16 bits probably is not sensible. 23 | */ 24 | typedef uint16_t milliseconds_t; 25 | /** Largest milliseconds_t number possible */ 26 | static constexpr milliseconds_t MILLISECONDS_T_MAX = static_cast(-1); 27 | 28 | /** 29 | * Type for modulation frequency in Hz. 30 | */ 31 | typedef uint32_t frequency_t; 32 | 33 | /** 34 | * Type for duty cycle in percent. Less than 0 means no information/don't care. 35 | */ 36 | typedef int8_t dutycycle_t; 37 | 38 | /** 39 | * Type for GPIO pin, compatible with Arduino libs. 40 | */ 41 | typedef uint8_t pin_t; 42 | /** Symbolic name for an invalid pin number */ 43 | static constexpr pin_t invalidPin = 255; 44 | /** Largest pin_t number possible */ 45 | static constexpr pin_t PIN_T_MAX = 255; 46 | -------------------------------------------------------------------------------- /src/IrSenderPwmSoftDelay.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #include 19 | 20 | #include "IrSenderPwmSoftDelay.h" 21 | 22 | IrSenderPwmSoftDelay::IrSenderPwmSoftDelay(pin_t outputPin) : IrSenderPwmSoft(outputPin) { 23 | } 24 | 25 | void inline IrSenderPwmSoftDelay::sleepMicros(microseconds_t us) { 26 | if (us > 0U) // Is this necessary? (Official docu https://www.arduino.cc/en/Reference/DelayMicroseconds does not tell.) 27 | Board::delayMicroseconds(us); 28 | } 29 | 30 | void inline IrSenderPwmSoftDelay::sleepUntilMicros(uint32_t targetTime) { 31 | int32_t time = targetTime - micros(); 32 | if (time < 0) 33 | return; 34 | delay(time/1000UL); 35 | int32_t rest = targetTime - micros(); 36 | if (rest >= 4) 37 | ::delayMicroseconds(rest); 38 | } 39 | -------------------------------------------------------------------------------- /src/boards/NoBoard.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "Board.h" 21 | 22 | #define CURRENT_CLASS NoBoard 23 | 24 | #define HAS_FLASH_READ 0 25 | #define HAS_HARDWARE_PWM 0 26 | #define HAS_SAMPLING 0 27 | #define HAS_INPUT_CAPTURE 0 28 | 29 | class NoBoard : public Board { 30 | #define PWM_PIN Board::NO_PIN 31 | public: 32 | NoBoard() {}; 33 | 34 | private: 35 | void timerEnableIntr() { 36 | }; 37 | 38 | void timerDisableIntr() { 39 | }; 40 | 41 | void timerEnablePwm() { 42 | }; 43 | 44 | void timerDisablePwm() { 45 | }; 46 | 47 | void timerConfigHz(frequency_t hz __attribute__ ((unused)), dutycycle_t dutyCycle __attribute__ ((unused))) { 48 | }; 49 | 50 | void timerConfigNormal() { 51 | }; 52 | }; 53 | -------------------------------------------------------------------------------- /src/MultiDecoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IrReader.h" 4 | #include "IrDecoder.h" 5 | 6 | /** 7 | * A preliminary multi protocol decoder. Tries the Nec1- and the Rc5 decoders. 8 | */ 9 | class MultiDecoder : public IrDecoder { 10 | public: 11 | /** 12 | * Enum over possible outcomes of the decoder. 13 | */ 14 | enum Type { 15 | timeout, ///< beginTimeout reached 16 | noise, ///< nothing sensible found 17 | undecoded, ///< decoding failed 18 | nec, ///< NEC1 intro 19 | nec_ditto, ///< NEC1 repeat 20 | rc5 ///< RC5 signal (= repeat sequence) 21 | }; 22 | 23 | private: 24 | char decode[17]; 25 | Type type; 26 | 27 | public: 28 | Type getType() const { 29 | return type; 30 | } 31 | 32 | /** 33 | * Constructs a MultiDecoder from an IrReader, containing data. 34 | * @param irReader IrReader with data, i.e. with isReady() true. 35 | */ 36 | MultiDecoder(const IrReader &irReader); 37 | 38 | #ifndef DOXYGEN 39 | MultiDecoder() = delete; 40 | MultiDecoder(const MultiDecoder&) = delete; 41 | MultiDecoder(MultiDecoder&&) = delete; 42 | MultiDecoder& operator=(const MultiDecoder&) = delete; 43 | MultiDecoder& operator=(MultiDecoder&&) = delete; 44 | #endif // ! DOXYGEN 45 | 46 | virtual ~MultiDecoder() { 47 | } 48 | 49 | const char *getDecode() const { 50 | return decode; 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /src/HashDecoder.cpp: -------------------------------------------------------------------------------- 1 | #include "HashDecoder.h" 2 | 3 | uint32_t HashDecoder::compare(microseconds_t oldVal, microseconds_t newVal) { 4 | return 5 | newVal < 3 * (oldVal / 4) ? 0 6 | : newVal > 5 * (oldVal / 4) ? 2 7 | : 1; 8 | } 9 | 10 | bool HashDecoder::tryDecode(const IrReader& irCapturer, Stream& stream) { 11 | HashDecoder decoder(irCapturer); 12 | return decoder.printDecode(stream); 13 | } 14 | 15 | void HashDecoder::decode(const microseconds_t* data, size_t length) { 16 | if (length < minMeaningfulLength) 17 | return; 18 | 19 | for (unsigned int i = 0; i < length - offset - 1; i++) { 20 | uint32_t value = compare(data[i], data[i + offset]); 21 | hash = (hash * FNVprime) ^ value; 22 | } 23 | 24 | setValid(true); 25 | } 26 | 27 | void HashDecoder::decode(const IrReader& irReader) { 28 | size_t length = irReader.getDataLength(); 29 | if (length < minMeaningfulLength) 30 | return; 31 | 32 | for (unsigned int i = 0; i < length - offset - 1; i++) { 33 | uint32_t value = compare(irReader.getDuration(i), irReader.getDuration(i + offset)); 34 | hash = (hash * FNVprime) ^ value; 35 | } 36 | 37 | setValid(true); 38 | } 39 | 40 | uint32_t HashDecoder::decodeHash(const IrSequence& irSequence) { 41 | HashDecoder decoder(irSequence); 42 | return decoder.getHash(); 43 | } 44 | 45 | uint32_t HashDecoder::decodeHash(const IrReader& irReader) { 46 | HashDecoder decoder(irReader); 47 | return decoder.getHash(); 48 | } -------------------------------------------------------------------------------- /examples/IrSenderPwmSpinWait/IrSenderPwmSpinWait.ino: -------------------------------------------------------------------------------- 1 | // This sketch sends a raw signal using the soft PWM sender every 5 seconds. 2 | // It requires an IR-Led connected to the sending pin 3 | // On slow processors, it may produce erroneous result, 4 | // in particular modulation frequency. 5 | 6 | #include 7 | 8 | static constexpr frequency_t necFrequency = 38400U; 9 | static constexpr pin_t pin = 10 | #ifdef ESP8266 11 | 4U; // D2 on ESP8266 12 | #else 13 | 3U; 14 | #endif 15 | static constexpr uint32_t BAUD = 115200UL; 16 | 17 | // NEC(1) 122 29 with no repetition; powers on many Yamaha receivers 18 | static const microseconds_t array[] = { 19 | 9024, 4512, 564, 564, 564, 1692, 564, 564, 564, 1692, 564, 1692, 20 | 564, 1692, 564, 1692, 564, 564, 564, 1692, 564, 564, 564, 1692, 21 | 564, 564, 564, 564, 564, 564, 564, 564, 564, 1692, 564, 1692, 564, 22 | 564, 564, 1692, 564, 1692, 564, 1692, 564, 564, 564, 564, 564, 564, 23 | 564, 564, 564, 1692, 564, 564, 564, 564, 564, 564, 564, 1692, 564, 24 | 1692, 564, 1692, 564, 39756 25 | }; 26 | 27 | static const IrSequence irSequence(array, sizeof(array) / sizeof(microseconds_t)); 28 | static IrSender* irSender; 29 | 30 | void setup() { 31 | Serial.begin(BAUD); 32 | while (!Serial) 33 | ; 34 | irSender = new IrSenderPwmSpinWait(pin); 35 | } 36 | 37 | void loop() { 38 | Serial.print(F(" sending @ pin ")); 39 | Serial.println(pin, DEC); 40 | irSender->send(irSequence, necFrequency); 41 | delay(5000); 42 | } 43 | -------------------------------------------------------------------------------- /examples/Pronto/Pronto.ino: -------------------------------------------------------------------------------- 1 | // Demonstrates the Pronto class: Three different forms of parsing 2 | // (RAM string, explicit PROGMEM, F(.)-form), 3 | // as well as the output function. 4 | 5 | #include 6 | #include 7 | 8 | #define PRONTOSTRING "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 0016 0016 0041 0016 0041 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0041 0016 0041 0016 0041 0016 05F7 015B 0057 0016 0E6C" 9 | uint32_t BAUD = 115200UL; 10 | 11 | const IrSignal *irSignal; 12 | const char PROGMEM data[] = PRONTOSTRING; 13 | 14 | void setup() { 15 | Serial.begin(BAUD); 16 | while(!Serial) 17 | ; 18 | 19 | // Read a pronto string from RAM (normally highly wasteful with RAM). 20 | irSignal = Pronto::parse(PRONTOSTRING); 21 | irSignal->dump(Serial, true); 22 | 23 | #if HAS_FLASH_READ 24 | // Read a signal from (explicit) PROGMEM 25 | irSignal = Pronto::parse_PF(data); 26 | irSignal->dump(Serial, true); 27 | 28 | // Use the F(.) form 29 | irSignal = Pronto::parse(F(PRONTOSTRING)); 30 | irSignal->dump(Serial, true); 31 | #endif 32 | 33 | irSignal = Nec1Renderer::newIrSignal(122, 27); // volume_down for Yahama receivers 34 | Serial.println(Pronto::toProntoHex(*irSignal)); 35 | Pronto::dump(Serial, *irSignal); 36 | Serial.println(); 37 | } 38 | 39 | void loop() { 40 | } 41 | -------------------------------------------------------------------------------- /examples/IrReceiverSampler/IrReceiverSampler.ino: -------------------------------------------------------------------------------- 1 | // This sketch demonstrates the IrReceiverSampler. 2 | // It requires a demodulating sensor connected to pin RECEIVE_PIN. 3 | 4 | #include 5 | #include 6 | 7 | #ifdef ESP32 8 | static constexpr pin_t RECEIVE_PIN = 4U; 9 | #elif defined(ARDUINO_AVR_MICRO) 10 | static constexpr pin_t RECEIVE_PIN = 10U; 11 | #else 12 | static constexpr pin_t RECEIVE_PIN = 5U; 13 | #endif 14 | 15 | static constexpr size_t BUFFERSIZE = 200U; 16 | static constexpr uint32_t BAUD = 115200UL; 17 | 18 | #ifdef ARDUINO_AVR_NANO 19 | static constexpr pin_t IRRECEIVER_1_GND = 6U; 20 | static constexpr pin_t IRRECEIVER_1_VCC = 7U; 21 | #endif 22 | #ifdef ARDUINO_AVR_MICRO 23 | static constexpr pin_t IRRECEIVER_1_GND = 16U; 24 | static constexpr pin_t IRRECEIVER_1_VCC = 14U; 25 | #endif 26 | 27 | static IrReceiver *receiver; 28 | 29 | void setup() { 30 | #ifdef IRRECEIVER_1_GND 31 | pinMode(IRRECEIVER_1_GND, OUTPUT); 32 | digitalWrite(IRRECEIVER_1_GND, LOW); 33 | #endif 34 | 35 | #ifdef IRRECEIVER_1_VCC 36 | pinMode(IRRECEIVER_1_VCC, OUTPUT); 37 | digitalWrite(IRRECEIVER_1_VCC, HIGH); 38 | #endif 39 | Serial.begin(BAUD); 40 | while (!Serial) 41 | ; 42 | 43 | receiver = IrReceiverSampler::newIrReceiverSampler(BUFFERSIZE, RECEIVE_PIN); 44 | Serial.print("Listening on pin "); 45 | Serial.println(receiver->getPin(), DEC); 46 | } 47 | 48 | void loop() { 49 | receiver->receive(); // combines enable, loop, disable 50 | if (receiver->isEmpty()) 51 | Serial.println("timeout"); 52 | else { 53 | receiver->dump(Serial); 54 | Serial.println(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/IrSenderNonMod/IrSenderNonMod.ino: -------------------------------------------------------------------------------- 1 | // This sketch demonstrates the usage of the non-modulating sender. 2 | // It requires an sender like TX-433 connected to pin NON_MOD_PIN 3 | 4 | // Turn on and off an Arctech device with house code M = 13, device 8. 5 | 6 | #include 7 | 8 | static constexpr pin_t NON_MOD_PIN = 10U; 9 | 10 | // Arctech D=13, S=8, F=0 (Generated by IrScrutinizer) 11 | static const microseconds_t offData[] = { 12 | 388, 1164, 388, 1164, 388, 1164, 388, 1164, 388, 1164, 1164, 388, 388, 13 | 1164, 1164, 388, 388, 1164, 1164, 388, 388, 1164, 1164, 388, 388, 1164, 14 | 1164, 388, 388, 1164, 388, 1164, 388, 1164, 388, 1164, 388, 1164, 1164, 15 | 388, 388, 1164, 1164, 388, 388, 1164, 388, 1164, 388, 11364 16 | }; 17 | static const IrSequence off(offData, sizeof(offData)/sizeof(microseconds_t)); 18 | 19 | // Arctech D=13, S=8, F=1 (Generated by IrScrutinizer) 20 | static const microseconds_t onData[] = { 21 | 388, 1164, 388, 1164, 388, 1164, 388, 1164, 388, 1164, 1164, 388, 388, 22 | 1164, 1164, 388, 388, 1164, 1164, 388, 388, 1164, 1164, 388, 388, 1164, 23 | 1164, 388, 388, 1164, 388, 1164, 388, 1164, 388, 1164, 388, 1164, 1164, 24 | 388, 388, 1164, 1164, 388, 388, 1164, 1164, 388, 388, 11364 25 | }; 26 | static const IrSequence on(onData, sizeof(onData)/sizeof(microseconds_t)); 27 | 28 | static IrSenderNonMod sender(NON_MOD_PIN); 29 | 30 | void setup() { 31 | } 32 | 33 | void loop() { 34 | // send it twice, since Intertechno devices seems to need that 35 | // for reliability 36 | sender.sendNonModulated(on, 2); 37 | delay(2000); 38 | sender.sendNonModulated(off, 2); 39 | delay(2000); 40 | } 41 | -------------------------------------------------------------------------------- /src/IrSenderPwmSoft.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include "IrSenderPwm.h" 22 | 23 | /** 24 | * Sending function using timer PWM. Due to the nature of the timers, this is a Highlander, 25 | * ("There can only be one"), so the class is a singleton class, with private constructor, 26 | * a factory method that enforces the "highlander property". 27 | */ 28 | class IrSenderPwmSoft : public IrSenderPwm { 29 | protected: 30 | IrSenderPwmSoft(pin_t outputPin); // no default! 31 | virtual ~IrSenderPwmSoft() {} 32 | void enable(frequency_t hz, dutycycle_t dutyCycle = Board::defaultDutyCycle); 33 | void sendMark(microseconds_t time); 34 | static constexpr unsigned int PULSE_CORRECTION = 3U; 35 | 36 | virtual void sleepMicros(microseconds_t us) = 0; 37 | virtual void sleepUntilMicros(uint32_t terminateTime) = 0; 38 | microseconds_t periodTime; 39 | microseconds_t periodOnTime; 40 | microseconds_t periodOffTime; 41 | }; 42 | -------------------------------------------------------------------------------- /src/Nec1Renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "Nec1Renderer.h" 2 | 3 | // NOTE: writing intro[i++] = ... produces wrong result, compiler bug? 4 | // (Adding a print statement immediately after, and it works :-~) 5 | // So let's write intro[i] = ...; i++ at least for now. 6 | 7 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 8 | 9 | const IrSignal *Nec1Renderer::newIrSignal(unsigned int D, unsigned int S, unsigned int F) { 10 | microseconds_t *introData = new microseconds_t[introLength]; 11 | unsigned int i = 0U; 12 | uint32_t sum = 9024U + 4512U + 564U; 13 | introData[i] = 9024U; i++; 14 | introData[i] = 4512U; i++; 15 | lsbByte(introData, i, sum, D); 16 | lsbByte(introData, i, sum, S); 17 | lsbByte(introData, i, sum, F); 18 | lsbByte(introData, i, sum, 255U-F); 19 | introData[i] = 564U; i++; 20 | introData[i] = static_cast(108000UL - sum); i++; 21 | microseconds_t *repeatData = new microseconds_t[repeatLength]; 22 | repeatData[0] = 9024U; 23 | repeatData[1] = 2256U; 24 | repeatData[2] = 564U; 25 | repeatData[3] = MIN(96156U, MICROSECONDS_T_MAX); 26 | return new IrSignal(introData, introLength, repeatData, repeatLength, frequency, dutyCycle); 27 | } 28 | 29 | void Nec1Renderer::lsbByte(microseconds_t *intro, unsigned int& i, uint32_t& sum, unsigned int X) { 30 | for (unsigned int index = 0; index < 8U; index++) { 31 | transmitBit(intro, i, sum, X & 1U); 32 | X >>= 1U; 33 | } 34 | } 35 | 36 | void inline Nec1Renderer::transmitBit(microseconds_t *intro, unsigned int& i, uint32_t& sum, unsigned int data) { 37 | intro[i++] = 564U; 38 | intro[i++] = data ? 1692U : 564U; 39 | sum += data ? 564U+1692U : 564U+564U; 40 | } 41 | -------------------------------------------------------------------------------- /src/IrSenderNonMod.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include "InfraredTypes.h" 22 | #include "IrSender.h" 23 | 24 | /** 25 | * IrSender implementation without modulation, selectively with inverted output. 26 | * Usage is, for example, controlling an RF transmitter (non-inverting), 27 | * or for emulating an IR receiver with active low output (inverting). 28 | */ 29 | class IrSenderNonMod : public IrSender { 30 | private: 31 | bool const invert; 32 | void enable(frequency_t frequency __attribute__((unused)), dutycycle_t d __attribute__((unused)) = Board::defaultDutyCycle) {}; 33 | void sendSpace(microseconds_t time); 34 | void sendMark(microseconds_t time); 35 | 36 | public: 37 | IrSenderNonMod(pin_t pin, bool invert = false); 38 | 39 | /** 40 | * Sends the IrSequence as argument. 41 | * @param irSequence 42 | * @param times Number of times to send, default 1. 43 | */ 44 | void sendNonModulated(const IrSequence& irSequence, unsigned int times = 1U); 45 | }; 46 | -------------------------------------------------------------------------------- /src/Nec1Renderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /** 6 | * A static class consisting of two functions that generate IrSignal-s from the NEC1 protocol parameters. 7 | * The NEC1 protocol is given in IRP notation as 8 | * \c {38.4k,564}<1,-1|1,-3>(16,-8,D:8,S:8,F:8,~F:8,1,^108m,(16,-4,1,^108m)*) [D:0..255,S:0..255=255-D,F:0..255] 9 | */ 10 | class Nec1Renderer { 11 | private: 12 | static constexpr frequency_t frequency = 38400UL; 13 | static constexpr dutycycle_t dutyCycle = 33U; 14 | static constexpr size_t introLength = 68U; 15 | static constexpr size_t repeatLength = 4U; 16 | 17 | public: 18 | 19 | /** 20 | * Generates am IrSignal from the NEC1 parameters. 21 | * Must be deleted explicitly by the user to avoid memory leaks. 22 | * @param D parameter in NEC1, "device" 23 | * @param S parameter in NEC1, "sub-device" 24 | * @param F parameter in NEC1, "function" 25 | * @return IrSignal 26 | */ 27 | static const IrSignal *newIrSignal(unsigned int D, unsigned int S, unsigned int F); 28 | 29 | /** 30 | * Generates am IrSignal from the NEC1 parameters. Equivalent to newIrSignal(D, 255-D, F). 31 | * Must be deleted explicitly by the user to avoid memory leaks. 32 | * @param D parameter in NEC1, "device" 33 | * @param F parameter in NEC1, "function" 34 | * @return IrSignal 35 | */ 36 | static const IrSignal *newIrSignal(unsigned int D, unsigned int F) { 37 | return newIrSignal(D, 255-D, F); 38 | }; 39 | 40 | private: 41 | Nec1Renderer(); 42 | static void lsbByte(microseconds_t *intro, unsigned int& i, uint32_t& sum, unsigned int D); 43 | static void transmitBit(microseconds_t *intro, unsigned int& i, uint32_t& sum, unsigned int data); 44 | }; 45 | -------------------------------------------------------------------------------- /src/Rc5Decoder.cpp: -------------------------------------------------------------------------------- 1 | #include "Rc5Decoder.h" 2 | #include 3 | 4 | Rc5Decoder::Length Rc5Decoder::decodeDuration(microseconds_t t) { 5 | Length len = (t < timebaseLower) ? invalid 6 | : (t <= timebaseUpper) ? half 7 | : (t >= 2*timebaseLower && t <= 2*timebaseUpper) ? full 8 | : invalid; 9 | return len; 10 | } 11 | 12 | unsigned int Rc5Decoder::decodeFlashGap(microseconds_t flash, microseconds_t gap) { 13 | bool result = getDuration(flash, 1); 14 | if (!result) 15 | return invalid; 16 | 17 | return getDuration(gap, 3) ? 1 18 | : getDuration(gap, 1) ? 0 19 | : invalid; 20 | } 21 | 22 | bool Rc5Decoder::tryDecode(const IrReader& irCapturer, Stream& stream) { 23 | Rc5Decoder decoder(irCapturer); 24 | return decoder.printDecode(stream); 25 | } 26 | 27 | Rc5Decoder::Rc5Decoder(const IrReader& irCapturer) { 28 | unsigned int index = 0U; 29 | unsigned int sum = 0U; 30 | int doublet = -1; 31 | decode[0] = '\0'; 32 | 33 | while (doublet < 25) { 34 | Length length = decodeDuration(irCapturer.getDuration(index++)); 35 | if (length == invalid) 36 | return; 37 | doublet += (int) length; 38 | if (doublet % 2 == 1) 39 | sum = (sum << 1U) + (index & 1U); 40 | } 41 | sum = ~sum & 0x1FFFU; 42 | 43 | bool success = isEnding(irCapturer.getDuration(irCapturer.getDataLength()-1)); 44 | if (!success) 45 | return; 46 | 47 | F = (sum & 0x3FU) | ((~sum & 0x1000U) >> 6U); 48 | D = (sum & 0x7C0U) >> 6U; 49 | T = (sum & 0x0800U) >> 11U; 50 | 51 | setValid(true); 52 | sprintf(decode, format, D, F, T); 53 | } 54 | 55 | const char *Rc5Decoder::getDecode() const { 56 | return decode; 57 | } 58 | -------------------------------------------------------------------------------- /src/IrSenderPwm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #include "IrSenderPwm.h" 19 | 20 | #ifdef HAS_HARDWARE_PWM 21 | #include "IrSenderPwmHard.h" 22 | #else 23 | #include "IrSenderPwmSoftDelay.h" 24 | #endif 25 | 26 | IrSenderPwm *IrSenderPwm::instance = nullptr; 27 | 28 | IrSenderPwm::IrSenderPwm(pin_t outputPin) : IrSender(outputPin) { 29 | } 30 | 31 | IrSenderPwm *IrSenderPwm::newInstance(pin_t outputPin) { 32 | if (instance != nullptr) 33 | return nullptr; 34 | instance = 35 | #ifdef HAS_HARDWARE_PWM 36 | IrSenderPwmHard::newInstance(outputPin); 37 | #else 38 | new IrSenderPwmSoftDelay(outputPin); 39 | #endif 40 | return instance; 41 | } 42 | 43 | IrSenderPwm *IrSenderPwm::getInstance(bool create, pin_t outputPin) { 44 | if (instance == nullptr && create) 45 | instance = newInstance(outputPin); 46 | return instance; 47 | } 48 | 49 | void IrSenderPwm::deleteInstance() { 50 | if (instance != nullptr) 51 | #ifdef HAS_HARDWARE_PWM 52 | IrSenderPwmHard::deleteInstance(); 53 | #else 54 | delete instance; 55 | #endif 56 | instance = nullptr; 57 | } 58 | -------------------------------------------------------------------------------- /examples/allreaders/allreaders.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static IrReader *reader; 7 | constexpr unsigned int captureLength = 200U; 8 | constexpr pin_t receiverPin = 5U; 9 | constexpr microseconds_t markExcess = 50U; 10 | 11 | void setup() { 12 | Serial.begin(115200UL); 13 | Serial.setTimeout(10000U); 14 | Serial.flush(); 15 | Serial.println(F("Enter s for sampler, p for poll, w for widget")); 16 | String line = Serial.readStringUntil('\r'); 17 | char ch = line.charAt(0); 18 | Serial.println(F("Enter beginning timeout in milliseconds (default 5000)")); 19 | long beginningTimeout = Serial.parseInt(); 20 | if (beginningTimeout == 0U) 21 | beginningTimeout = 5000U; 22 | Serial.println(F("Enter ending timeout in milliseconds (default 30)")); 23 | long endingTimeout = Serial.parseInt(); 24 | if (endingTimeout == 0U) 25 | endingTimeout = 30U; 26 | 27 | reader = ch == 's' 28 | ? (IrReader*) IrReceiverSampler::newIrReceiverSampler(captureLength, receiverPin, 29 | false, markExcess, beginningTimeout, endingTimeout) 30 | : ch == 'p' 31 | ? (IrReader*) new IrReceiverPoll(captureLength, receiverPin, 32 | false, markExcess, beginningTimeout, endingTimeout) 33 | : (IrReader*) IrWidgetAggregating::newIrWidgetAggregating(captureLength, 34 | false, markExcess, beginningTimeout, endingTimeout); 35 | Serial.println(F("Now fire IR signals at your sensor.")); 36 | } 37 | 38 | void loop() { 39 | reader->receive(); 40 | if (reader->isEmpty()) 41 | Serial.println(F("timeout")); 42 | else { 43 | reader->dump(Serial); 44 | Serial.println(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/IrReceiver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "InfraredTypes.h" 5 | #include "IrReader.h" 6 | #include "IrSignal.h" 7 | #include "Board.h" 8 | 9 | /** 10 | * Abstract base class for demodulating IR receivers. 11 | */ 12 | class IrReceiver : public IrReader { 13 | private: 14 | /** GPIO pin the receiver is connected to. */ 15 | pin_t pin; 16 | 17 | public: 18 | // Default values 19 | static constexpr pin_t defaultPin = 5; 20 | static constexpr microseconds_t defaultMarkExcess = 50U; 21 | 22 | /** Are we using inverting sensor, like most TSOPs? */ 23 | static constexpr bool invertingSensor = true; 24 | 25 | /** 26 | * Constructor. 27 | * 28 | * @param bufSize 29 | * @param pin GPIO pin to be used. Will be setup by the constructor. 30 | * @param pullup Enable hardware pullup within the processor. 31 | * @param markExcess 32 | */ 33 | IrReceiver(size_t bufSize, pin_t pin, bool pullup = false, 34 | microseconds_t markExcess = defaultMarkExcess); 35 | 36 | virtual ~IrReceiver() { 37 | }; 38 | 39 | virtual frequency_t getFrequency() const { 40 | return IrSignal::defaultFrequency; 41 | }; 42 | 43 | virtual void receive(); 44 | 45 | pin_t getPin() const { 46 | return pin; 47 | } 48 | 49 | /** 50 | * Enum for the duration types. 51 | */ 52 | enum irdata_t { 53 | IR_MARK, ///< on-period, also called flash 54 | IR_SPACE ///< off-period, also called gap 55 | }; 56 | 57 | // Needs to be public since used in ISP. Therefore hide it from Doxygen 58 | /// @cond false 59 | irdata_t readIr() { 60 | return (Board::getInstance()->readDigital(getPin()) ^ invertingSensor) ? IR_MARK : IR_SPACE; 61 | } 62 | /// @endcond 63 | }; 64 | -------------------------------------------------------------------------------- /src/Rc5Renderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IrSignal.h" 4 | /** 5 | * A static class consisting of two functions that generate IrSignal-s from the RC5 protocol parameters. 6 | * The RC5 protocol is given in IRP notation as 7 | * \c {36k,msb,889}<1,-1|-1,1>((1:1,~F:1:6,T:1,D:5,F:6,^114m)+,T=1-T)[T\@:0..1=0,D:0..31,F:0..127] 8 | */ 9 | class Rc5Renderer { 10 | private: 11 | static constexpr frequency_t frequency = 36000UL; 12 | static constexpr size_t introLength = 0U; 13 | static constexpr size_t endingLength = 0U; 14 | 15 | public: 16 | /** 17 | * Generates an RC5 signal from the RC5 parameters. 18 | * Must be deleted explicitly by the user to avoid memory leaks. 19 | * @param D RC5 parameter, "device" 20 | * @param F RC5 parameter, "function" 21 | * @param T RC5 parameter, "toggle" 22 | * @return IrSignal 23 | */ 24 | static const IrSignal *newIrSignal(unsigned int D, unsigned int F, unsigned int T); 25 | 26 | /** 27 | * Generates an RC5 signal from the RC5 parametes. 28 | * This version uses an internal toggle of the class to compute T. 29 | * Must be deleted explicitly by the user to avoid memory leaks. 30 | * @param D RC5 parameter, "device" 31 | * @param F RC5 parameter, "function" 32 | * @return IrSignal 33 | */ 34 | static const IrSignal *newIrSignal(unsigned int D, unsigned int F); 35 | 36 | private: 37 | Rc5Renderer(); 38 | static constexpr microseconds_t timebase = 889U; 39 | static void emit(unsigned int t, unsigned int& index, int& pending, microseconds_t *repeat); 40 | static void emitMsb(unsigned int x, unsigned int length, unsigned int& index, 41 | int& pending, microseconds_t *repeat); 42 | static void emitEnd(unsigned int& index, int& pending, microseconds_t *repeat); 43 | 44 | static uint8_t T; 45 | }; 46 | -------------------------------------------------------------------------------- /examples/IrSignal/IrSignal.ino: -------------------------------------------------------------------------------- 1 | // Just a trivial exercise on creating and printing of IrSignal 2 | 3 | #include 4 | #include // for HAS_FLASH_READ 5 | 6 | static const uint32_t BAUD = 115200UL; 7 | 8 | static const microseconds_t intro[] = { 9 | 9041, 4507, 573, 573, 573, 573, 573, 1694, 573, 1694, 573, 573, 573, 10 | 573, 573, 573, 573, 573, 573, 573, 573, 1694, 573, 573, 573, 573, 11 | 573, 573, 573, 1694, 573, 573, 573, 573, 573, 573, 573, 573, 573, 12 | 573, 573, 1694, 573, 1694, 573, 1694, 573, 573, 573, 573, 573, 1694, 13 | 573, 1694, 573, 1694, 573, 573, 573, 573, 573, 573, 573, 1694, 573, 14 | 1694, 573, 44293U 15 | }; 16 | 17 | static const microseconds_t repeat[] = { 9041, 2267, 573, 65535U }; 18 | 19 | IrSignal irSignal(intro, sizeof(intro)/sizeof(microseconds_t), 20 | repeat, sizeof(repeat)/sizeof(microseconds_t), 38400U); 21 | 22 | #if HAS_FLASH_READ 23 | static const microseconds_t intro_pm[] PROGMEM = { 24 | 9041, 4507, 573, 573, 573, 573, 573, 1694, 573, 1694, 573, 573, 573, 25 | 573, 573, 573, 573, 573, 573, 573, 573, 1694, 573, 573, 573, 573, 26 | 573, 573, 573, 1694, 573, 573, 573, 573, 573, 573, 573, 573, 573, 27 | 573, 573, 1694, 573, 1694, 573, 1694, 573, 573, 573, 573, 573, 1694, 28 | 573, 1694, 573, 1694, 573, 573, 573, 573, 573, 573, 573, 1694, 573, 29 | 1694, 573, 44293U 30 | }; 31 | 32 | static const microseconds_t repeat_pm[] PROGMEM = { 9041, 2267, 573, 65535U }; 33 | 34 | IrSignal* irSignal_pm = IrSignal::readFlash(intro_pm, sizeof(intro_pm)/sizeof(microseconds_t), 35 | repeat_pm, sizeof(repeat_pm)/sizeof(microseconds_t), 38400U); 36 | #endif 37 | 38 | void setup() { 39 | Serial.begin(BAUD); 40 | while(!Serial) 41 | ; 42 | irSignal.dump(Serial, true); 43 | #ifdef HAS_FLASH_READ 44 | irSignal_pm->dump(Serial, true); 45 | #endif 46 | } 47 | 48 | void loop() { 49 | } 50 | -------------------------------------------------------------------------------- /src/PinModeStatus.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020, 2021 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | /** 19 | * @file PinModeStatus.h 20 | * @brief This file make sure that the types PinMode and PinStatus, 21 | * as well as the values HIGH and LOW are availabled. 22 | * To test this, we use the defined-ness of LOW. 23 | * 24 | * Some, but not all, architectures defines PinStatus and PinMode in a file like 25 | * ~/.arduino15/packages/arduino/hardware/.../cores/arduino/api/Common.h 26 | * 27 | * See also https://github.com/arduino/ArduinoCore-API/issues/25 28 | */ 29 | 30 | #pragma once 31 | 32 | #if ! defined(ARDUINO) 33 | 34 | typedef enum { 35 | LOW = 0, 36 | HIGH = 1, 37 | CHANGE = 2, 38 | FALLING = 3, 39 | RISING = 4, 40 | } PinStatus; 41 | 42 | typedef enum { 43 | INPUT = 0x0, 44 | OUTPUT = 0x1, 45 | INPUT_PULLUP = 0x2, 46 | INPUT_PULLDOWN = 0x3, 47 | } PinMode; 48 | 49 | #elif defined(LOW) || defined(DOXYGEN)// ARDUINO 50 | 51 | /** Values of a digital pin. */ 52 | typedef int PinStatus; 53 | 54 | /** Different operating modes of a GPIO pin. */ 55 | typedef int PinMode; 56 | 57 | #else 58 | 59 | // Is Arduino, but LOW is not defined. 60 | // We assume that PinStatus and PinMode are defined as enums. 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /examples/IrSenderPwm/IrSenderPwm.ino: -------------------------------------------------------------------------------- 1 | // This sketch sends a raw signal using the PWM sender every 5 seconds. 2 | // It requires an IR-Led connected to the sending pin 3 | // (3 on Uno/Nano, 9 on Leonardo/Uno, 9 on Mega2560 etc...) 4 | // Just for the fun of it, the duty cycle is selected randomly. 5 | 6 | #include 7 | 8 | static constexpr frequency_t necFrequency = 38400U; 9 | static constexpr unsigned long BAUD = 115200UL; 10 | static constexpr pin_t PIN = 3U; 11 | 12 | // NEC(1) 122 29 with no repetition; powers on many Yamaha receivers 13 | static const microseconds_t array[] = { 14 | 9024, 4512, 564, 564, 564, 1692, 564, 564, 564, 1692, 564, 1692, 15 | 564, 1692, 564, 1692, 564, 564, 564, 1692, 564, 564, 564, 1692, 16 | 564, 564, 564, 564, 564, 564, 564, 564, 564, 1692, 564, 1692, 564, 17 | 564, 564, 1692, 564, 1692, 564, 1692, 564, 564, 564, 564, 564, 564, 18 | 564, 564, 564, 1692, 564, 564, 564, 564, 564, 564, 564, 1692, 564, 19 | 1692, 564, 1692, 564, 39756 20 | }; 21 | 22 | static const IrSequence irSequence(array, sizeof(array) / sizeof(microseconds_t)); 23 | static IrSender* irSender; 24 | 25 | void setup() { 26 | Serial.begin(BAUD); 27 | while (!Serial) 28 | ; 29 | if (Board::getInstance()->hasHardwarePwm()) 30 | Serial.println(F("Hardware PWM available!")); 31 | else 32 | Serial.println(F("Hardware PWM NOT available, will be emulated in software.")); 33 | randomSeed(analogRead(A0)); 34 | irSender = IrSenderPwm::getInstance(true, PIN); 35 | } 36 | 37 | void loop() { 38 | dutycycle_t dutyCycle;dutyCycle = static_cast(random(20,80)); 39 | Serial.print(F("Shooting @ pin ")); 40 | Serial.print(irSender->getPin(), DEC); 41 | Serial.print(F(" with duty cycle ")); 42 | Serial.print(dutyCycle, DEC); 43 | Serial.println("%"); 44 | irSender->send(irSequence, necFrequency, dutyCycle); 45 | delay(5000); 46 | } 47 | -------------------------------------------------------------------------------- /src/IrDecoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "InfraredTypes.h" 4 | 5 | /** 6 | * Abstract base class for all decoder classes. 7 | */ 8 | class IrDecoder { 9 | public: 10 | IrDecoder() {} 11 | 12 | #ifndef DOXYGEN 13 | IrDecoder(const IrDecoder&) = delete; 14 | IrDecoder(IrDecoder&&) = delete; 15 | IrDecoder& operator=(const IrDecoder&) = delete; 16 | IrDecoder& operator=(IrDecoder&&) = delete; 17 | #endif // ! DOXYGEN 18 | 19 | virtual ~IrDecoder() {} 20 | 21 | /** 22 | * Returns a textual description the decode for human consumption. 23 | * @return decode as text. 24 | */ 25 | virtual const char *getDecode() const = 0; 26 | 27 | /** 28 | * Returns true if the decode was successful. 29 | * @return validity 30 | */ 31 | virtual bool isValid() const { 32 | return valid; 33 | } 34 | 35 | /** 36 | * Same as isValid(). 37 | * @return 38 | */ 39 | operator bool() const { 40 | return isValid(); 41 | } 42 | 43 | /** 44 | * If valid, prints the decode to the stream. 45 | * @param stream where the output is generated 46 | * @return success status 47 | */ 48 | bool printDecode(Stream& stream) const { 49 | if (isValid()) 50 | stream.println(getDecode()); 51 | return isValid(); 52 | } 53 | 54 | private: 55 | static constexpr uint32_t endingMin = 20000UL; 56 | bool valid = false; 57 | 58 | protected: 59 | constexpr static int invalid = -1; 60 | void setValid(bool valid_) { 61 | valid = valid_; 62 | } 63 | 64 | /** 65 | * Tests if the argument is large enough to be considered an ending of a decodable signal. 66 | * @param duration time to be tested 67 | * @return true if the duration is long enough 68 | */ 69 | static bool isEnding(microseconds_t duration) { 70 | return duration > endingMin; 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /src/IrSenderPwmHard.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #include 19 | #include "Board.h" 20 | 21 | #ifdef HAS_HARDWARE_PWM 22 | #include "IrSenderPwmHard.h" 23 | 24 | IrSenderPwmHard *IrSenderPwmHard::instance = nullptr; 25 | 26 | IrSenderPwmHard::IrSenderPwmHard(pin_t outputPin __attribute__((unused))) : IrSenderPwm(PWM_PIN) { 27 | } 28 | 29 | IrSenderPwmHard::~IrSenderPwmHard() { 30 | disable(); 31 | } 32 | 33 | IrSenderPwmHard *IrSenderPwmHard::newInstance(pin_t outputPin) { 34 | if (instance != nullptr) 35 | return nullptr; 36 | instance = new IrSenderPwmHard(outputPin); 37 | return instance; 38 | } 39 | 40 | IrSenderPwmHard *IrSenderPwmHard::getInstance(bool create, pin_t outputPin) { 41 | if (instance == nullptr && create) 42 | instance = new IrSenderPwmHard(outputPin); 43 | return instance; 44 | } 45 | 46 | void IrSenderPwmHard::enable(frequency_t frequency, dutycycle_t dutyCycle) { 47 | Board::getInstance()->enablePwm(getPin(), frequency, dutyCycle); 48 | } 49 | 50 | void IrSenderPwmHard::disable() { 51 | Board::getInstance()->disablePwm(); 52 | } 53 | 54 | void inline IrSenderPwmHard::sendMark(milliseconds_t time) { 55 | Board::getInstance()->sendPwmMark(time); 56 | } 57 | 58 | #endif // HAS_HARDWARE_PWM 59 | -------------------------------------------------------------------------------- /src/IrSenderPwm.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include "IrSender.h" 22 | #include "Board.h" 23 | 24 | /** 25 | * Sending function using timer PWM. Due to the nature of the timers, this is a Highlander, 26 | * ("There can only be one"), so the class is a singleton class, with private constructor, 27 | * a factory method that enforces the "highlander property". 28 | */ 29 | class IrSenderPwm : public IrSender { 30 | private: 31 | static constexpr unsigned int defaultDutyCycle = 50U; 32 | static IrSenderPwm *instance; 33 | 34 | protected: 35 | IrSenderPwm(pin_t sendPin); 36 | virtual ~IrSenderPwm() {} 37 | 38 | public: 39 | static unsigned int getDutyCycle() { return defaultDutyCycle; }; 40 | 41 | /** 42 | * Returns a pointer to the instance, or nullptr if not initialized. 43 | * If argument true, in the latter case creates a new instance and returns it. 44 | */ 45 | static IrSenderPwm *getInstance(bool create = false, pin_t outputPin = Board::getInstance()->defaultPwmPin()); 46 | 47 | /** 48 | * Creates a new instance (if not existing) and returns it. 49 | * Returns nullptr if an instance already exists. 50 | */ 51 | static IrSenderPwm *newInstance(pin_t outputPin); 52 | 53 | static void deleteInstance(); 54 | }; 55 | -------------------------------------------------------------------------------- /src/IrReceiverPoll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IrReceiver.h" 4 | 5 | /** 6 | * @class IrReceiverPoll 7 | * An implementation of IrReceiver using polling of the input pin. 8 | * It uses no timer or other hardware resources, and should thus run 9 | * on all platforms. 10 | */ 11 | class IrReceiverPoll : public IrReceiver { 12 | private: 13 | /** Data buffer */ 14 | microseconds_t *durationData; 15 | 16 | /** Number of valid entries in durationData */ 17 | size_t dataLength; 18 | 19 | public: 20 | IrReceiverPoll(size_t captureLength = defaultCaptureLength, 21 | pin_t pin = defaultPin, 22 | bool pullup = false, 23 | microseconds_t markExcess = defaultMarkExcess, 24 | milliseconds_t beginningTimeout = defaultBeginningTimeout, 25 | milliseconds_t endingTimeout = defaultEndingTimeout); 26 | 27 | ~IrReceiverPoll(); 28 | 29 | bool isReady() const { 30 | return timeouted || !isEmpty(); 31 | } 32 | 33 | void reset(); 34 | 35 | size_t getDataLength() const { 36 | return dataLength; 37 | } 38 | 39 | microseconds_t getDuration(unsigned int i) const { 40 | return durationData[i]; 41 | } 42 | 43 | void setEndingTimeout(milliseconds_t timeOut) { 44 | endingTimeout = timeOut; 45 | } 46 | 47 | milliseconds_t getEndingTimeout() const { 48 | return endingTimeout; 49 | } 50 | 51 | milliseconds_t getBeginningTimeout() const { 52 | return beginningTimeout; 53 | } 54 | 55 | void setBeginningTimeout(milliseconds_t timeOut) { 56 | beginningTimeout = timeOut; 57 | } 58 | 59 | /** 60 | * In this class, enable does the actual collection of the data. 61 | * It returns when either the signal is captured, or (beginning) 62 | * timeout occurs. 63 | */ 64 | void enable(); 65 | 66 | void disable() {}; 67 | 68 | private: 69 | bool searchForStart(); 70 | 71 | void collectData(); 72 | 73 | void recordDuration(unsigned long t); 74 | }; 75 | -------------------------------------------------------------------------------- /src/Rc5Decoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IrDecoder.h" 4 | #include "IrReader.h" 5 | 6 | /** 7 | * A decoder class for RC5 signals. 8 | */ 9 | class Rc5Decoder : public IrDecoder { 10 | public: 11 | 12 | /** 13 | * Constructs a Rc5Decoder from an IrReader, containing data. 14 | * @param irReader IrReader with data, i.e. with isReady() true. 15 | */ 16 | Rc5Decoder(const IrReader& irReader); 17 | 18 | virtual ~Rc5Decoder() { 19 | } 20 | 21 | /** 22 | * Returns the F parameter, or -1 if invalid. 23 | * @return int 24 | */ 25 | int getF() const { 26 | return F; 27 | } 28 | 29 | /** 30 | * Returns the D parameter, or -1 if invalid. 31 | * @return int 32 | */ 33 | int getD() const { 34 | return D; 35 | } 36 | 37 | /** 38 | * Returns the T parameter, or -1 if invalid. 39 | * @return int 40 | */ 41 | int getT() const { 42 | return T; 43 | } 44 | 45 | /** 46 | * Convenience function; constructs an Rc5Decoder and calls its printDecode. 47 | * @param irReader IrReader to use 48 | * @param stream Stream 49 | * @return success of operation 50 | */ 51 | static bool tryDecode(const IrReader& irReader, Stream& stream); 52 | 53 | const char *getDecode() const; 54 | 55 | private: 56 | char decode[13]; 57 | const static microseconds_t timebase = 889U; 58 | const static microseconds_t timebaseLower = 800U; 59 | const static microseconds_t timebaseUpper = 1000U; 60 | static bool getDuration(microseconds_t duration, unsigned int time) { 61 | return duration <= time * timebaseUpper 62 | && duration >= time * timebaseLower; 63 | } 64 | int F; 65 | int D; 66 | int T; 67 | 68 | enum Length { 69 | invalid = 0, 70 | half = 1, 71 | full = 2 72 | }; 73 | 74 | static constexpr const char *format = "RC5 %d %d %d"; 75 | 76 | static Length decodeDuration(microseconds_t t); 77 | static unsigned int decodeFlashGap(microseconds_t flash, microseconds_t gap); 78 | }; 79 | -------------------------------------------------------------------------------- /src/IrWidgetAggregating.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Michael Dreher 2 | // this code may be distributed under the terms of the General Public License V2 (GPL V2) 3 | 4 | // This is a slight reorganization of the original code, by Bengt Martensson. 5 | 6 | #pragma once 7 | 8 | #include "IrWidget.h" 9 | 10 | /** 11 | * This class implements the IrWidget. It delivers the duration and an estimate of the 12 | * modulation frequency based upon the collected samples. Since it uses a single timer, 13 | * it is singleton class, only instantiable by the factory method newIrWidgetAggregating. 14 | */ 15 | class IrWidgetAggregating : public IrWidget { 16 | private: 17 | static IrWidgetAggregating *instance; 18 | IrWidgetAggregating() { 19 | } 20 | 21 | public: 22 | void capture(); 23 | static void deleteInstance(); 24 | 25 | static IrWidgetAggregating *getInstance() { 26 | return instance; 27 | } 28 | 29 | static IrWidgetAggregating *newIrWidgetAggregating(size_t captureLength = defaultCaptureLength, 30 | bool pullup = false, 31 | int16_t markExcess = defaultMarkExcess, 32 | milliseconds_t beginningTimeout = defaultBeginningTimeout, 33 | milliseconds_t endingTimeout = defaultEndingTimeout); 34 | 35 | protected: 36 | IrWidgetAggregating(size_t captureLength = defaultCaptureLength, 37 | bool pullup = false, 38 | int16_t markExcess = defaultMarkExcess, 39 | milliseconds_t beginningTimeout = defaultBeginningTimeout, 40 | milliseconds_t endingTimeout = defaultEndingTimeout); 41 | 42 | private: 43 | inline uint16_t packTimeVal/*Normal*/(uint32_t val) const { 44 | if (val >= 0x8000) { 45 | val = val >> (RANGE_EXTENSION_BITS + 1); 46 | val |= 0x8000; 47 | } 48 | 49 | return val; 50 | } 51 | 52 | inline uint32_t unpackTimeVal/*Normal*/(uint32_t val) const { 53 | if (val & 0x8000) { 54 | val = (val & 0x7fff) << (RANGE_EXTENSION_BITS + 1); 55 | } 56 | 57 | return val; 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /src/IrSequence.cpp: -------------------------------------------------------------------------------- 1 | #include "IrSequence.h" 2 | #include "Board.h" 3 | #include 4 | 5 | IrSequence::IrSequence(const microseconds_t *durations_, size_t length_) : durations(durations_),length(length_) { 6 | } 7 | 8 | IrSequence::IrSequence(IrSequence&& orig) : durations(orig.durations),length(orig.length) { 9 | orig.durations = nullptr; 10 | orig.length = 0U; 11 | } 12 | 13 | IrSequence::IrSequence(const IrSequence& orig) : length(orig.length) { 14 | microseconds_t* data = new microseconds_t[orig.length]; 15 | for (unsigned int i = 0U; i < orig.length; i++) 16 | data[i] = orig.durations[i]; 17 | durations = data; 18 | } 19 | 20 | IrSequence& IrSequence::operator=(IrSequence&& rhs) { 21 | if (this != &rhs) { 22 | delete [] durations; 23 | durations = rhs.durations; 24 | length = rhs.length; 25 | rhs.durations = nullptr; 26 | rhs.length = 0; 27 | } 28 | return *this; 29 | } 30 | 31 | IrSequence& IrSequence::operator=(const IrSequence& rhs) { 32 | delete [] durations; 33 | microseconds_t* data = new microseconds_t[rhs.length]; 34 | for (unsigned int i = 0U; i < rhs.length; i++) 35 | data[i] = rhs.durations[i]; 36 | durations = data; 37 | length = rhs.length; 38 | return *this; 39 | } 40 | 41 | IrSequence::~IrSequence() { 42 | delete [] durations; 43 | } 44 | 45 | const IrSequence IrSequence::emptyInstance; 46 | 47 | void IrSequence::dump(Stream& stream, bool usingSigns) const { 48 | for (unsigned int i = 0U; i < length; i++) { 49 | if (i > 0U) 50 | stream.print(' '); 51 | if (usingSigns) 52 | stream.print((i & 1) ? '-' : '+'); 53 | stream.print(durations[i], DEC); 54 | } 55 | stream.println(); 56 | } 57 | 58 | // If ! HAS_FLASH_READ, allow compiling, but let linking bail out, if using it. 59 | #if HAS_FLASH_READ 60 | IrSequence* IrSequence::readFlash(const microseconds_t *flashData, size_t length) { 61 | microseconds_t* data = new microseconds_t[length]; 62 | memcpy_PF(data, (uint_farptr_t) flashData, sizeof(microseconds_t) * length); 63 | return new IrSequence(data, length); 64 | } 65 | #endif -------------------------------------------------------------------------------- /src/boards/Sam.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include "InfraredTypes.h" 21 | 22 | #define CURRENT_CLASS Sam 23 | 24 | #define HAS_FLASH_READ 1 25 | #define HAS_HARDWARE_PWM 1 26 | #define HAS_SAMPLING 1 27 | #define HAS_INPUT_CAPTURE 0 28 | 29 | #define STRCPY_PF_CAST(x) static_cast(x) 30 | 31 | // Default PWM pin 32 | #define PWM_PIN 3 33 | 34 | #ifdef ISR 35 | #undef ISR 36 | #endif 37 | #define ISR(f) void interruptServiceRoutine() 38 | 39 | class Sam : public Board { 40 | public: 41 | 42 | Sam() { 43 | }; 44 | 45 | private: 46 | pin_t pwmPin; 47 | static constexpr bool invert = false; 48 | uint16_t maxValue; 49 | uint16_t onLength; 50 | bool timerTCC0; 51 | bool timerTCC1; 52 | bool timerTCC2; 53 | Tcc* TCCx; 54 | 55 | void timerConfigNormal(); 56 | 57 | void timerEnableIntr(); 58 | 59 | void timerDisableIntr(); 60 | 61 | void timerConfigHz(frequency_t hz __attribute__ ((unused)), dutycycle_t dutyCycle __attribute__ ((unused))) { 62 | timerConfigHz(PWM_PIN, hz, dutyCycle); 63 | }; 64 | 65 | void timerConfigHz(pin_t pin, frequency_t hz, dutycycle_t dutyCycle); 66 | 67 | void timerEnablePwm() { 68 | setValue(onLength); 69 | }; 70 | 71 | void timerDisablePwm() { 72 | setValue(0U); 73 | } 74 | 75 | static const unsigned int TIMER_PRESCALER_DIV = 64U; 76 | 77 | void setTimerFrequency(frequency_t hz); 78 | 79 | void setValue(uint16_t value); 80 | }; 81 | -------------------------------------------------------------------------------- /src/Rc5Renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "Rc5Renderer.h" 2 | 3 | // NOTE: writing intro[i++] = ... produces wrong result, compiler bug? 4 | // (Adding a print statement immediately after, and it works :-~) 5 | // So let's write intro[i] = ...; i++ at least for now. 6 | 7 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 8 | 9 | uint8_t Rc5Renderer::T = 1U; 10 | 11 | const IrSignal *Rc5Renderer::newIrSignal(unsigned int D, unsigned int F) { 12 | T = ! T; 13 | return newIrSignal(D, F, T); 14 | } 15 | 16 | static const IrSequence emptyIrSequence; 17 | 18 | const IrSignal *Rc5Renderer::newIrSignal(unsigned int D, unsigned int F, unsigned int T) { 19 | unsigned int index = 0U; 20 | int pending = 0; 21 | microseconds_t *repeat = new microseconds_t[28]; 22 | emit(1U, index, pending, repeat); 23 | emit(((~F) & 0x40U) >> 6U, index, pending, repeat); 24 | emit(T & 1U, index, pending, repeat); 25 | emitMsb(D, 5U, index, pending, repeat); 26 | emitMsb(F, 6U, index, pending, repeat); 27 | emitEnd(index, pending, repeat); 28 | return new IrSignal(nullptr, 0, repeat, index, frequency); 29 | } 30 | 31 | void Rc5Renderer::emitMsb(unsigned int x, unsigned int length, 32 | unsigned int& index, int& pending, microseconds_t *repeat) { 33 | unsigned int mask = 1U << (length - 1U); 34 | while (mask != 0U) { 35 | emit((x & mask) != 0, index, pending, repeat); 36 | mask >>= 1U; 37 | } 38 | } 39 | 40 | void Rc5Renderer::emit(unsigned int x, unsigned int& index, int& pending, 41 | microseconds_t *repeat) { 42 | if (pending == 0) { 43 | // First, do nothing, just stuff in pending. 44 | } else if ((pending > 0) == ((x & 1U) != 0U)) { 45 | repeat[index] = timebase; 46 | index++; 47 | repeat[index] = timebase; 48 | index++; 49 | } else { 50 | repeat[index] = 2U * timebase; 51 | index++; 52 | } 53 | pending = (x & 1U) ? 1 : -1; 54 | } 55 | 56 | void Rc5Renderer::emitEnd(unsigned int& index, int& pending, microseconds_t *repeat) { 57 | if (pending > 0) { 58 | repeat[index] = timebase; index++; 59 | } 60 | repeat[index] = MIN(90000U, MICROSECONDS_T_MAX); index++; 61 | } 62 | -------------------------------------------------------------------------------- /src/IrReceiverPoll.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "IrReceiverPoll.h" 3 | 4 | IrReceiverPoll::IrReceiverPoll(size_t captureLength, 5 | pin_t pin_, 6 | bool pullup, 7 | microseconds_t markExcess, 8 | milliseconds_t beginningTimeout, 9 | milliseconds_t endingTimeout) : IrReceiver(captureLength, pin_, pullup, markExcess) { 10 | setBeginningTimeout(beginningTimeout); 11 | setEndingTimeout(endingTimeout); 12 | durationData = new microseconds_t[bufferSize]; 13 | dataLength = 0; 14 | } 15 | 16 | IrReceiverPoll::~IrReceiverPoll() { 17 | delete [] durationData; 18 | // let the pin stay as input 19 | } 20 | 21 | void IrReceiverPoll::reset() { 22 | IrReader::reset(); 23 | dataLength = 0; 24 | } 25 | 26 | void IrReceiverPoll::enable() { 27 | reset(); 28 | timeouted = !searchForStart(); 29 | if (timeouted) 30 | return; 31 | collectData(); 32 | } 33 | 34 | unsigned long timeSince(unsigned long then) { 35 | return micros() - then; 36 | } 37 | 38 | bool IrReceiverPoll::searchForStart() { 39 | unsigned long start = micros(); 40 | unsigned long beginningTimeoutInMicros = 1000UL * beginningTimeout; 41 | while (readIr() == IrReceiver::IR_SPACE) { 42 | if (timeSince(start) > beginningTimeoutInMicros) 43 | return false; 44 | yield(); 45 | } 46 | return true; 47 | } 48 | 49 | void IrReceiverPoll::collectData() { 50 | IrReceiver::irdata_t lastDataRead = IrReceiver::IR_MARK; 51 | unsigned long endingTimeoutInMicros = 1000UL * endingTimeout; 52 | unsigned long lastTime = micros(); 53 | while (dataLength < bufferSize) { 54 | IrReceiver::irdata_t data = readIr(); 55 | if (data != lastDataRead) { 56 | unsigned long now = micros(); 57 | recordDuration(now - lastTime); 58 | lastDataRead = data; 59 | lastTime = now; 60 | } else if (data == IrReceiver::IR_SPACE) { 61 | unsigned long now = micros(); 62 | if (now - lastTime > endingTimeoutInMicros) { 63 | recordDuration(now - lastTime); 64 | return; // normal exit 65 | } 66 | } 67 | yield(); 68 | } 69 | } 70 | 71 | void IrReceiverPoll::recordDuration(unsigned long t) { 72 | durationData[dataLength++] = t <= MICROSECONDS_T_MAX ? (microseconds_t) t : MICROSECONDS_T_MAX; 73 | } 74 | -------------------------------------------------------------------------------- /src/Nec1Decoder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IrDecoder.h" 4 | #include "IrReader.h" 5 | 6 | /** 7 | * A decoder class for NEC1 signals. 8 | */ 9 | class Nec1Decoder : public IrDecoder { 10 | private: 11 | static constexpr microseconds_t timebase = 564U; 12 | static constexpr microseconds_t timebaseUpper = 650U; 13 | static constexpr microseconds_t timebaseLower = 450U; 14 | 15 | // NOTE: use a signed type to be able to return the value invalid. 16 | int F; 17 | int D; 18 | int S; 19 | bool ditto; 20 | 21 | char decode[17]; 22 | 23 | static bool getDuration(microseconds_t duration, unsigned int time) { 24 | return duration <= time * timebaseUpper 25 | && duration >= time * timebaseLower; 26 | } 27 | static int decodeParameter(const IrReader &irCapturer, unsigned int index); 28 | static int decodeFlashGap(microseconds_t flash, microseconds_t gap); 29 | static constexpr const char *nec1DittoLiteral = "NEC1 ditto"; 30 | 31 | public: 32 | Nec1Decoder(); 33 | 34 | /** 35 | * Constructs a Nec1Decoder from an IrReader, containing data. 36 | * @param irReader IrReader with data, i.e. with isReady() true. 37 | */ 38 | Nec1Decoder(const IrReader& irReader); 39 | virtual ~Nec1Decoder() {}; 40 | 41 | /** 42 | * Returns the F parameter, or -1 if invalid. 43 | * @return int 44 | */ 45 | int getF() const { 46 | return F; 47 | } 48 | 49 | /** 50 | * Returns the D parameter, or -1 if invalid. 51 | * @return int 52 | */ 53 | int getD() const { 54 | return D; 55 | } 56 | 57 | /** 58 | * Returns the S parameter, or -1 if invalid. 59 | * @return int 60 | */ 61 | int getS() const { 62 | return S; 63 | } 64 | 65 | /** 66 | * Returns true if the signal received is a NEC1 ditto, i,e. a repeat sequence. 67 | * @return true if repeat sequence 68 | */ 69 | bool isDitto() const { 70 | return ditto; 71 | }; 72 | 73 | /** 74 | * Convenience function; constructs an Nec1Decoder and calls its printDecode. 75 | * @param irReader IrReader to use 76 | * @param stream Stream 77 | * @return success of operation 78 | */ 79 | static bool tryDecode(const IrReader &irReader, Stream& stream); 80 | 81 | const char *getDecode() const { 82 | return decode; 83 | }; 84 | 85 | }; 86 | -------------------------------------------------------------------------------- /src/IrSenderPwmHard.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include "IrSenderPwm.h" 22 | #include "Board.h" 23 | 24 | #ifndef HAS_HARDWARE_PWM 25 | #error Current board does not support hardware PWM and thus not the class IrSendPwm. Consider using IrSenderSoftPwm or IrSenderSpin instead. 26 | #endif 27 | 28 | /** 29 | * Sending function using timer PWM. Due to the nature of the timers, this is a Highlander, 30 | * ("There can only be one"), so the class is a singleton class, with private constructor, 31 | * a factory method that enforces the "highlander property". 32 | */ 33 | class IrSenderPwmHard : public IrSenderPwm { 34 | public: 35 | IrSenderPwmHard(pin_t outputPin = Board::getInstance()->defaultPwmPin()); 36 | virtual ~IrSenderPwmHard(); 37 | 38 | private: 39 | static IrSenderPwmHard *instance; 40 | void enable(frequency_t frequency, dutycycle_t dutyCycle = Board::defaultDutyCycle); 41 | void disable(); 42 | //void sendSpace(microseconds_t time); 43 | void sendMark(microseconds_t time); 44 | 45 | static void barfForInvalidPin(pin_t outputPin) { if (outputPin != Board::getInstance()->defaultPwmPin()) {/*error("Silly pin")*/};}; 46 | 47 | public: 48 | /** 49 | * Returns a pointer to the instance, or nullptr if not initialized. 50 | * If argument true, in the latter case creates a new instance and returns it. 51 | */ 52 | static IrSenderPwmHard *getInstance(bool create = false, pin_t ouputPin = Board::getInstance()->defaultPwmPin()); 53 | 54 | /** 55 | * Creates a new instance (if not existing) and returns it. 56 | * Returns nullptr if an instance already exists. 57 | */ 58 | static IrSenderPwmHard *newInstance(pin_t ouputPin = Board::getInstance()->defaultPwmPin()); 59 | 60 | static void deleteInstance() { 61 | delete instance; 62 | instance = nullptr; 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /src/IrSenderPwmSoft.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #include 19 | #include "IrSenderPwmSoft.h" 20 | 21 | IrSenderPwmSoft::IrSenderPwmSoft(pin_t outputPin) : IrSenderPwm(outputPin) { 22 | } 23 | 24 | //void IrSenderPwmSoft::sendSpace(milliseconds_t time) { 25 | // writeLow(); 26 | // delayUSecs(time); 27 | //} 28 | 29 | //void inline IrSenderPwmSoft::sleepMicros(unsigned long us) { 30 | //#ifdef USE_SPIN_WAIT 31 | // sleepUntilMicros(micros() + us); 32 | //#else 33 | // if (us > 0U) // Is this necessary? (Official docu https://www.arduino.cc/en/Reference/DelayMicroseconds does not tell.) 34 | // delayMicroseconds((unsigned int) us); 35 | //#endif 36 | //} 37 | 38 | //void IrSenderPwmSoft::sleepUntilMicros(unsigned long targetTime) { 39 | //#ifdef USE_SPIN_WAIT 40 | // while (micros() < targetTime) 41 | // ; 42 | //#else 43 | // sleepMicros(targetTime - micros()); 44 | //#endif 45 | //} 46 | 47 | void IrSenderPwmSoft::enable(frequency_t hz, dutycycle_t dutyCycle) { 48 | writeLow(); 49 | periodTime = (1000000UL + hz / 2L) / hz; // = 1000000/hz + 1/2 = round(1000000.0/hz) 50 | periodOnTime = periodTime * dutyCycle / 100U /*- pulseCorrection*/; 51 | periodOffTime = periodTime - periodOnTime; 52 | } 53 | 54 | void IrSenderPwmSoft::sendMark(microseconds_t time) { 55 | unsigned long start = micros(); 56 | unsigned long stop = start + time; 57 | if (stop + periodTime < start) 58 | // Counter wrap-around, happens very seldomly, but CAN happen. 59 | // Just give up instead of possibly damaging the hardware. 60 | return; 61 | 62 | //unsigned long nextPeriodEnding = start; 63 | unsigned long now = micros(); 64 | while (now < stop) { 65 | unsigned long then = now + periodTime; 66 | writeHigh(); 67 | sleepMicros(periodOnTime); 68 | writeLow(); 69 | //nextPeriodEnding += periodTime; 70 | sleepUntilMicros(then); 71 | now = micros(); 72 | } 73 | } -------------------------------------------------------------------------------- /src/IrSender.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #include "IrSender.h" 19 | #include "IrSignal.h" 20 | 21 | IrSender::IrSender(pin_t pin) : sendPin(pin) { 22 | Board::getInstance()->setPinMode(pin, OUTPUT); 23 | Board::getInstance()->writeLow(pin); 24 | } 25 | 26 | IrSender::~IrSender() { 27 | mute(); 28 | } 29 | 30 | void IrSender::sendIrSignal(const IrSignal& irSignal, unsigned int noSends) { 31 | send(irSignal.getIntro(), irSignal.getFrequency()); 32 | for (unsigned int i = 0; i < irSignal.noRepetitions(noSends); i++) 33 | send(irSignal.getRepeat(), irSignal.getFrequency()); 34 | send(irSignal.getEnding(), irSignal.getFrequency()); 35 | } 36 | 37 | void IrSender::sendWhile(const IrSignal& irSignal, bool(*trigger)()) { 38 | if (trigger()) { 39 | // "Button is pressed", send 40 | 41 | // Send the intro sequence,... 42 | sendIrSignal(irSignal, 1); 43 | 44 | // ... then, for as long as the button is held, 45 | // send the repeat sequence 46 | while (trigger()) { 47 | send(irSignal.getRepeat()); 48 | } 49 | 50 | // finally, the ending sequence (normally empty) 51 | send(irSignal.getEnding()); 52 | } else { 53 | // Button is not pressed, do nothing. 54 | } 55 | } 56 | 57 | void IrSender::send(const IrSequence& irSequence, frequency_t frequency, dutycycle_t dutyCycle) { 58 | enable(frequency, dutyCycle); 59 | #ifdef CONSIDER_COMPUTATIONAL_DELAYS 60 | uint32_t refTime = micros(); 61 | #endif 62 | for (unsigned int i = 0U; i < irSequence.getLength(); i++) { 63 | #ifdef CONSIDER_COMPUTATIONAL_DELAYS 64 | #error not tested 65 | microseconds_t duration = irSequence[i]; 66 | refTime += duration; 67 | int32_t delay = refTime - micros(); // TODO verify overflow 68 | if (delay <= 0) 69 | return; 70 | #else 71 | microseconds_t delay = irSequence[i]; 72 | #endif 73 | if (i & 1U) 74 | sendSpace(delay); 75 | else 76 | sendMark(delay); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/boards/Esp32.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | /** 19 | * @file Esp32.h 20 | * 21 | * @brief Hardware dependent definitions for Esp32 boards. 22 | * Based upon https://github.com/z3t0/Arduino-IRremote/pull/540/files 23 | * by Sensorslot (Andreas Spiess), as well as 24 | * https://github.com/anothermist/LIBRARIES/blob/master/IRremote/esp32.cpp 25 | */ 26 | 27 | #pragma once 28 | 29 | #define CURRENT_CLASS Esp32 30 | 31 | #define HAS_FLASH_READ 0 32 | #define HAS_HARDWARE_PWM 1 33 | #define HAS_SAMPLING 1 34 | #define HAS_INPUT_CAPTURE 0 35 | 36 | #define STRCPY_PF_CAST(x) (x) 37 | 38 | //#ifndef LED_BUILTIN 39 | //#define LED_BUILTIN 2 40 | //#endif 41 | 42 | #define TIMER_SIZE 8 43 | 44 | #ifdef ISR 45 | #undef ISR 46 | #endif 47 | #define ISR(f) void ICACHE_RAM_ATTR IRTimer() 48 | 49 | void IRTimer(); // defined in IrReceiverSampler.cpp, masqueraded as ISR(TIMER_INTR_NAME) 50 | 51 | #define PWM_PIN G13 52 | 53 | class Esp32 : public Board { 54 | public: 55 | 56 | Esp32() { 57 | }; 58 | 59 | private: 60 | 61 | static hw_timer_t* timer; 62 | uint8_t onValue; 63 | 64 | void timerEnableIntr() { 65 | // Interrupt Service Routine - Fires every 50uS 66 | // ESP32 has a different timer API now. 67 | // Ask for a frequency, and it gives you a timer if one is available and capable. 68 | timer = timerBegin(1000000); 69 | timerAttachInterrupt(timer, IRTimer); 70 | // every 50 microseconds, autoreload = true 71 | timerAlarm(timer, microsPerTick, true, 0); 72 | }; 73 | 74 | void timerDisableIntr() { 75 | if (timer != nullptr) { 76 | timerDetachInterrupt(timer); 77 | timerEnd(timer); 78 | } 79 | }; 80 | 81 | void timerEnablePwm() { 82 | ledcWrite(PWM_PIN, onValue); 83 | }; 84 | 85 | void timerDisablePwm() { 86 | ledcWrite(PWM_PIN, 0); 87 | }; 88 | 89 | void timerConfigHz(frequency_t frequency, dutycycle_t dutyCycle) { 90 | onValue = static_cast(256U * dutyCycle / 100U); 91 | ledcAttach(PWM_PIN, frequency, TIMER_SIZE); 92 | }; 93 | 94 | void timerConfigNormal() { 95 | ledcDetach(PWM_PIN); 96 | } 97 | }; -------------------------------------------------------------------------------- /examples/IrReceiverSampler_SenderPwm/IrReceiverSampler_SenderPwm.ino: -------------------------------------------------------------------------------- 1 | // This sketch combines sending and receiving. 2 | // It is useful for testing by developers. 3 | // It is NOT intended as a pedagogical example of usage of the library. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef ESP32 10 | static constexpr pin_t RECEIVE_PIN = 4U; 11 | #elif defined(ESP8266) 12 | static constexpr pin_t RECEIVE_PIN = 2U; 13 | #elif defined(ARDUINO_AVR_MICRO) 14 | static constexpr pin_t RECEIVE_PIN = 10U; 15 | #elif defined (ARDUINO_TEENSY30) || defined (ARDUINO_TEENSY32) // Teenex 3.0-3.2 16 | static constexpr pin_t RECEIVE_PIN = 7U; 17 | #else 18 | static constexpr pin_t RECEIVE_PIN = 5U; 19 | #endif 20 | 21 | static constexpr size_t BUFFERSIZE = 200U; 22 | static constexpr uint32_t BAUD = 115200UL; 23 | 24 | #ifdef ARDUINO_AVR_NANO 25 | static constexpr pin_t IRRECEIVER_1_GND = 6U; 26 | static constexpr pin_t IRRECEIVER_1_VCC = 7U; 27 | #endif 28 | 29 | #ifdef ARDUINO_AVR_MICRO 30 | static constexpr pin_t IRRECEIVER_1_GND = 16U; 31 | static constexpr pin_t IRRECEIVER_1_VCC = 14U; 32 | #endif 33 | 34 | static constexpr frequency_t necFrequency = 38400U; 35 | 36 | // NEC(1) 122 29 with no repetition; powers on many Yamaha receivers 37 | static const microseconds_t array[] 38 | #if HAS_FLASH_READ 39 | PROGMEM 40 | #endif 41 | = { 42 | 9024, 4512, 564, 564, 564, 1692, 564, 564, 564, 1692, 564, 1692, 43 | 564, 1692, 564, 1692, 564, 564, 564, 1692, 564, 564, 564, 1692, 44 | 564, 564, 564, 564, 564, 564, 564, 564, 564, 1692, 564, 1692, 564, 45 | 564, 564, 1692, 564, 1692, 564, 1692, 564, 564, 564, 564, 564, 564, 46 | 564, 564, 564, 1692, 564, 564, 564, 564, 564, 564, 564, 1692, 564, 47 | 1692, 564, 1692, 564, 39756 48 | }; 49 | 50 | static const IrSequence* irSequence = 51 | #if HAS_FLASH_READ 52 | IrSequence::readFlash(array, sizeof (array) / sizeof (microseconds_t)); 53 | #else 54 | new IrSequence(array, sizeof (array) / sizeof (microseconds_t)); 55 | #endif 56 | static IrReceiver *receiver; 57 | 58 | void setup() { 59 | #ifdef IRRECEIVER_1_GND 60 | pinMode(IRRECEIVER_1_GND, OUTPUT); 61 | digitalWrite(IRRECEIVER_1_GND, LOW); 62 | #endif 63 | 64 | #ifdef IRRECEIVER_1_VCC 65 | pinMode(IRRECEIVER_1_VCC, OUTPUT); 66 | digitalWrite(IRRECEIVER_1_VCC, HIGH); 67 | #endif 68 | 69 | Serial.begin(BAUD); 70 | while(!Serial) 71 | ; 72 | #if HAS_FLASH_READ 73 | Serial.println(F("Sending a signal from PROGMEM!")); 74 | #else 75 | Serial.println(F("Sending a signal!")); 76 | #endif 77 | IrSender *sender = IrSenderPwm::getInstance(true); 78 | sender->send(*irSequence, necFrequency); 79 | 80 | receiver = IrReceiverSampler::newIrReceiverSampler(BUFFERSIZE, RECEIVE_PIN); 81 | Serial.println(F("Listening!")); 82 | receiver->enable(); 83 | } 84 | 85 | void loop() { 86 | receiver->receive(); // combines enable, loop, disable 87 | if (receiver->isEmpty()) { 88 | Serial.println("timeout"); 89 | } else { 90 | receiver->dump(Serial); 91 | Serial.println(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/IrSender.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include "IrSignal.h" 22 | #include "Board.h" 23 | 24 | /** 25 | * Abstract base class for all sending classes. 26 | */ 27 | class IrSender { 28 | private: 29 | pin_t sendPin; 30 | 31 | public: 32 | inline pin_t getPin() const { 33 | return sendPin; 34 | } 35 | 36 | protected: 37 | // TODO: Rewrite for efficiency 38 | inline void writeHigh() { Board::getInstance()->writeHigh(sendPin); }; 39 | inline void writeLow() { Board::getInstance()->writeLow(sendPin); }; 40 | 41 | IrSender(pin_t pin); 42 | 43 | // TODO: something sensible... 44 | /*virtual*/ static void barfForInvalidPin(pin_t sendPin __attribute__((unused))) {}; 45 | 46 | virtual void enable(frequency_t frequency, dutycycle_t dutyCycle = Board::defaultDutyCycle) = 0; 47 | virtual void sendSpace(microseconds_t time) { Board::delayMicroseconds(time); }; 48 | virtual void sendMark(microseconds_t time) = 0; 49 | 50 | public: 51 | #ifndef DOXYGEN 52 | IrSender() = delete; 53 | IrSender(const IrSender&) = delete; 54 | IrSender(IrSender&&) = delete; 55 | IrSender& operator=(const IrSender&) = delete; 56 | IrSender& operator=(IrSender&&) = delete; 57 | #endif // ! DOXYGEN 58 | 59 | virtual ~IrSender(); 60 | 61 | /** 62 | * Sends an IrSequence with the prescribed frequency 63 | * @param irSequence 64 | * @param frequency frequency in Hz 65 | * @param dutyCycle 66 | */ 67 | virtual void send(const IrSequence& irSequence, frequency_t frequency = IrSignal::defaultFrequency, dutycycle_t dutyCycle = Board::defaultDutyCycle); 68 | 69 | /** 70 | * Sends the IrSignal given as argument the prescribed number of times. 71 | * This will send the intro sequence signal of the IrSignal, noSend of the 72 | * repeat, alternatively noSends - 1 repeats if intro is non-empty, and finally 73 | * the ending sequence. 74 | * @param irSignal 75 | * @param noSends 76 | */ 77 | void sendIrSignal(const IrSignal& irSignal, unsigned int noSends = 1); 78 | 79 | /** 80 | * Send an IrSignal, when and as long as trigger() returns true. 81 | * @param irSignal 82 | * @param trigger Function returning bool. 83 | */ 84 | void sendWhile(const IrSignal& irSignal, bool(*trigger)()); 85 | 86 | /** Force output pin inactive. */ 87 | void mute() { writeLow(); }; 88 | }; 89 | -------------------------------------------------------------------------------- /examples/LearningHashDecoder/LearningHashDecoder.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static constexpr pin_t RECEIVE_PIN = 5U; 5 | static constexpr size_t BUFFERSIZE = 200U; 6 | static constexpr uint32_t BAUD = 115200UL; 7 | static constexpr uint32_t NecRepeatHash = 84696351UL; 8 | 9 | //#define DEBUG 10 | 11 | // Just 4 random names 12 | static const char* names[] = { 13 | "Play", 14 | "Stop", 15 | "Pause", 16 | "Rewind" 17 | }; 18 | 19 | class Command{ 20 | public : 21 | const char* name; 22 | uint32_t code; 23 | 24 | Command() : name(nullptr), code(0UL) {}; 25 | Command(const char* n, uint32_t c) : name(n),code(c) {} 26 | }; 27 | 28 | static constexpr unsigned numberCommands = sizeof(names) / sizeof(const char*); 29 | static Command *learnedCommands; 30 | static IrReceiver *receiver; 31 | 32 | void setup() { 33 | Serial.begin(BAUD); 34 | while (!Serial) 35 | ; 36 | 37 | Serial.print(F("Make sure you have a demodulating receiver at pin ")); 38 | Serial.println(RECEIVE_PIN); 39 | 40 | receiver = IrReceiverSampler::newIrReceiverSampler(BUFFERSIZE, RECEIVE_PIN); 41 | receiver->setEndingTimeout(20); 42 | 43 | learnedCommands = new Command[numberCommands]; 44 | 45 | for (unsigned i = 0; i < numberCommands; i++) { 46 | const char* name = names[i]; 47 | Serial.print(F("Now send the command we will call ")); 48 | Serial.print(name); 49 | Serial.print(F("...")); 50 | 51 | do 52 | receiver->receive(); 53 | while (receiver->isEmpty()); 54 | Serial.println(F("OK")); 55 | 56 | uint32_t hash = HashDecoder::decodeHash(*receiver); 57 | #ifdef DEBUG 58 | Serial.println(hash); 59 | #endif 60 | Command cmd(name, hash); 61 | learnedCommands[i] = cmd; 62 | delay(250); 63 | } 64 | 65 | #ifdef DEBUG 66 | Serial.println(F("Learned finished! Got the following table:")); 67 | for (unsigned i = 0; i < numberCommands; i++) { 68 | Serial.print(i); 69 | Serial.print('\t'); 70 | Command command = learnedCommands[i]; 71 | Serial.print(command.name); 72 | Serial.print('\t'); 73 | Serial.println(command.code); 74 | } 75 | #endif 76 | Serial.println(F("Now press these buttons, and occasionally other ones.")); 77 | Serial.println(); 78 | } 79 | 80 | void loop() { 81 | do 82 | receiver->receive(); 83 | while (receiver->isEmpty()); 84 | 85 | uint32_t hash = HashDecoder::decodeHash(*receiver); 86 | #ifdef DEBUG 87 | Serial.println(hash); 88 | #endif 89 | if (hash == NecRepeatHash) // NEC1 repeat, discard 90 | return; 91 | 92 | for (unsigned i = 0; i < numberCommands; i++) { 93 | Command cmd = learnedCommands[i]; 94 | if (cmd.code == hash) { 95 | Serial.print(F("Thank you for sending the ")); 96 | Serial.print(cmd.name); 97 | Serial.println(F(" command.")); 98 | return; 99 | } 100 | } 101 | Serial.println(F("Unknown command, please try again.")); 102 | } 103 | -------------------------------------------------------------------------------- /src/boards/ATmega4809.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | /** 19 | * @file ATmega4809.h 20 | * 21 | * @brief Hardware dependent definitions for boards based on the ATMega 4809. 22 | * This includes the Arduino Uno WiFi Rev. 2, Nano Every. 23 | * 24 | * This file is based upon https://github.com/z3t0/Arduino-IRremote/pull/657, 25 | * incorporating https://github.com/z3t0/Arduino-IRremote/pull/657#issuecomment-570900315 26 | * (second version) and 27 | * https://github.com/z3t0/Arduino-IRremote/pull/657#issuecomment-571841983 . 28 | */ 29 | 30 | #pragma once 31 | 32 | #define CURRENT_CLASS ATmega4809 33 | 34 | #define HAS_FLASH_READ 1 35 | #define HAS_HARDWARE_PWM 1 36 | #define HAS_SAMPLING 1 37 | #define HAS_INPUT_CAPTURE 0 38 | 39 | #define STRCPY_PF_CAST(x) (x) 40 | 41 | class ATmega4809 : public Board { 42 | public: 43 | ATmega4809() { 44 | }; 45 | 46 | private: 47 | 48 | #ifdef IR_USE_TIMER1 49 | /////////////////////////////////////////////////////////////////////////////// 50 | #define TIMER_INTR_NAME TCB0_INT_vect 51 | 52 | void timerReset() { 53 | TCB0.INTFLAGS = TCB_CAPT_bm; 54 | }; 55 | //#define TIMER_ENABLE_PWM (TCB0.CTRLB = TCB_CNTMODE_PWM8_gc) 56 | //#define TIMER_DISABLE_PWM (TCB0.CTRLB &= ~(TCB_CNTMODE_PWM8_gc)) 57 | 58 | void timerEnablePwm() { 59 | TCB0.CTRLB |= TCB_CCMPEN_bm; 60 | }; 61 | 62 | void timerDisablePwm() { 63 | TCB0.CTRLB &= ~(TCB_CCMPEN_bm); 64 | }; 65 | 66 | void timerEnableIntr() { 67 | TCB0.INTCTRL = TCB_CAPT_bm; 68 | }; 69 | 70 | void timerDisableIntr() { 71 | TCB0.INTCTRL &= ~TCB_CAPT_bm; 72 | }; 73 | 74 | void timerConfigHz(frequency_t frequency, dutycycle_t dutyCycle) { 75 | const uint8_t pwmval = F_CPU / 2U / frequency; 76 | TCB0.CTRLB = TCB_CNTMODE_PWM8_gc; 77 | TCB0.CCMPL = pwmval; 78 | TCB0.CCMPH = pwmval * dutyCycle / 100UL; 79 | TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc | TCB_ENABLE_bm; 80 | }; 81 | 82 | void timerConfigNormal() { 83 | TCB0.CTRLB = TCB_CNTMODE_INT_gc; 84 | TCB0.CCMP = F_CPU * microsPerTick / 1000000UL; 85 | TCB0.INTCTRL = TCB_CAPT_bm; 86 | TCB0.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; 87 | }; 88 | 89 | #define PWM_PIN 6 90 | 91 | /////////////////////////////////////////////////////////////////////////////// 92 | #elif defined(IR_USE_TIMER2) // ! defined(IR_USE_TIMER1) 93 | 94 | // TODO... ;-) 95 | #error IR_USE_TIMER2 for this architecture is not yet implemented, sorry. 96 | 97 | #else // ! defined(IR_USE_TIMER2) 98 | 99 | #error Config error, either IR_USE_TIMER1 or IR_USE_TIMER2 must be defined. 100 | 101 | #endif 102 | }; -------------------------------------------------------------------------------- /src/HashDecoder.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file HashDecoder.h 3 | * @brief This file defines a hash based "decoder class". 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "IrDecoder.h" 9 | #include "IrReader.h" 10 | #include "IrSignal.h" 11 | 12 | /** 13 | * A decoder class using FNV-1 14 | * hashes of length 32. 15 | * This is not a decoder in the proper sense of the word, 16 | * but instead computes a hash value from the IrSequence. 17 | * For different IrSequences, this will, with high probability, be different. 18 | */ 19 | class HashDecoder : public IrDecoder { 20 | private: 21 | uint32_t hash; 22 | 23 | char decodeBuffer[2 * sizeof (hash) + 1]; 24 | 25 | static uint32_t compare(microseconds_t oldval, microseconds_t newval); 26 | 27 | void decode(const microseconds_t* data, size_t length); 28 | 29 | void decode(const IrReader& irReader); 30 | 31 | void decode(const IrSequence& irSequence) { 32 | decode(irSequence.getDurations(), irSequence.getLength()); 33 | } 34 | 35 | void decode(const IrSignal& irSignal) { 36 | decode(irSignal.getIntro()); 37 | decode(irSignal.getRepeat()); 38 | decode(irSignal.getEnding()); 39 | } 40 | 41 | static constexpr unsigned int offset = 2; 42 | static constexpr const char *format = "%0lx"; 43 | static constexpr uint32_t FNVprime = 16777619UL; 44 | static constexpr uint32_t FNVoffsetBasis = 2166136261UL; 45 | static constexpr unsigned int minMeaningfulLength = 4U; 46 | 47 | public: 48 | 49 | #ifndef DOXYGEN 50 | HashDecoder() = delete; 51 | HashDecoder(const HashDecoder&) = delete; 52 | HashDecoder(HashDecoder&&) = delete; 53 | HashDecoder& operator=(const HashDecoder&) = delete; 54 | HashDecoder& operator=(HashDecoder&&) = delete; 55 | #endif // ! DOXYGEN 56 | 57 | virtual ~HashDecoder() {} 58 | 59 | /** 60 | * Constructs a HashDecoder from an IrReader, containing data. 61 | * @param irReader IrReader with data, i.e. with isReady() true. 62 | */ 63 | HashDecoder(const IrReader& irReader) : IrDecoder(),hash(FNVoffsetBasis) { 64 | decode(irReader); 65 | sprintf(decodeBuffer, format, static_cast(hash)); 66 | } 67 | 68 | /** 69 | * Constructs a HashDecoder from an IrSequence. 70 | * @param irSequence 71 | */ 72 | HashDecoder(const IrSequence& irSequence) : IrDecoder(),hash(FNVoffsetBasis) { 73 | decode(irSequence); 74 | sprintf(decodeBuffer, format, static_cast(hash)); 75 | } 76 | 77 | HashDecoder(const IrSignal& irSignal) : IrDecoder(),hash(FNVoffsetBasis) { 78 | decode(irSignal); 79 | sprintf(decodeBuffer, format, static_cast(hash)); 80 | } 81 | 82 | /** 83 | * Returns the hash value. 84 | * @return uint32_t 85 | */ 86 | uint32_t getHash() const { 87 | return hash; 88 | } 89 | 90 | static uint32_t decodeHash(const IrSequence& irSequence); 91 | 92 | static uint32_t decodeHash(const IrReader& irReader); 93 | 94 | /** 95 | * Convenience function; constructs a HashDecoder and calls its printDecode. 96 | * @param irReader IrReader to use 97 | * @param stream Stream 98 | * @return success of operation 99 | */ 100 | static bool tryDecode(const IrReader& irReader, Stream& stream); 101 | 102 | const char *getDecode() const { 103 | return decodeBuffer; 104 | } 105 | }; 106 | -------------------------------------------------------------------------------- /src/IrSequence.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "InfraredTypes.h" 4 | 5 | /** 6 | * This class consists of a vector of durations. The even entries denotes spaces, 7 | * while the odd entries denotes gaps. The length should always be even, i.e., 8 | * the sequences starts with a space, and ends with a gap. 9 | * This class is immutable (except for assignments). 10 | */ 11 | class IrSequence { 12 | private: 13 | const microseconds_t *durations = nullptr; 14 | size_t length = 0U; 15 | 16 | public: 17 | /** Create an empty sequence. */ 18 | IrSequence() {}; 19 | 20 | /** 21 | * Creates an IrSequence. 22 | * @param durations const array of microseconds durations. Will be "moved", and delete []-d by the destructor. 23 | * @param length length of durations. Should be even (not checked). 24 | */ 25 | IrSequence(const microseconds_t *durations, size_t length); 26 | 27 | virtual ~IrSequence(); 28 | 29 | /** 30 | * Copy constructor. 31 | * @param orig original IrSequence to be copied. 32 | */ 33 | IrSequence(const IrSequence& orig); 34 | 35 | /** 36 | * Move constructor. 37 | * @param orig original IrSequence to be moved 38 | */ 39 | IrSequence(IrSequence&& orig); 40 | 41 | /** 42 | * Copy assignment. 43 | * @param rhs 44 | */ 45 | IrSequence& operator=(const IrSequence& rhs); 46 | 47 | /** 48 | * Move assignment. 49 | * @param rhs 50 | */ 51 | IrSequence& operator=(IrSequence&& rhs); 52 | 53 | static const IrSequence emptyInstance; 54 | 55 | /** 56 | * True if and only if non-empty. 57 | * @return 58 | */ 59 | operator bool() const { return ! isEmpty(); } 60 | 61 | /** 62 | * Returns the number of durations. 63 | * @return length 64 | */ 65 | size_t getLength() const { 66 | return length; 67 | } 68 | 69 | bool isEmpty() const { 70 | return length == 0U; 71 | } 72 | 73 | const microseconds_t *getDurations() const { 74 | return durations; 75 | } 76 | 77 | size_t size() const { 78 | return length; 79 | } 80 | 81 | const microseconds_t* begin() const { 82 | return durations; 83 | } 84 | 85 | const microseconds_t* end() const { 86 | return durations + length; 87 | } 88 | 89 | microseconds_t operator[](int i) const { 90 | return durations[i]; 91 | } 92 | 93 | /** 94 | * Prints the IrSequence on the stream provided. 95 | * @param stream Stream onto the output is printed. 96 | * @param usingSigns If true, Gaps are written with a leading '+', spaces with a leading '-'. 97 | */ 98 | void dump(Stream& stream, bool usingSigns = false) const; 99 | 100 | /** 101 | * Prints the IrSequence on the stream provided. Gaps are written with a 102 | * leading '+', spaces with a leading '-'. 103 | * @param stream Stream onto the output is printed. 104 | */ 105 | void dumpWithSigns(Stream& stream) const { 106 | dump(stream, true); 107 | }; 108 | 109 | // Do NOT put #ifdef HAS_FLASH_READ here, leads to circular includes. 110 | /** 111 | * Create an IrSequence from data in PROGMEM. 112 | * This must manually be deleted by the programmer. 113 | * The function is available only on platforms implementing the memcpy_PF 114 | * call. 115 | * @param flashData 116 | * @param length 117 | * @return Pointer to IrSequence. 118 | */ 119 | static IrSequence* readFlash(const microseconds_t *flashData, size_t length); 120 | }; 121 | -------------------------------------------------------------------------------- /src/Nec1Decoder.cpp: -------------------------------------------------------------------------------- 1 | #include "Nec1Decoder.h" 2 | #include "IrReader.h" 3 | #include 4 | #include 5 | 6 | //{38.4k,564}<1,-1|1,-3>(16,-8,D:8,S:8,F:8,~F:8,1,^108m,(16,-4,1,^108m)*) [D:0..255,S:0..255=255-D,F:0..255] 7 | 8 | int Nec1Decoder::decodeFlashGap(microseconds_t flash, microseconds_t gap) { 9 | bool result = getDuration(flash, 1); 10 | if (!result) 11 | return invalid; 12 | 13 | return getDuration(gap, 3) ? 1 14 | : getDuration(gap, 1) ? 0 15 | : invalid; 16 | } 17 | 18 | bool Nec1Decoder::tryDecode(const IrReader& irReader, Stream& stream) { 19 | Nec1Decoder decoder(irReader); 20 | return decoder.printDecode(stream); 21 | } 22 | 23 | int Nec1Decoder::decodeParameter(const IrReader& irReader, unsigned int index) { 24 | unsigned int sum = 0; 25 | for (int i = 7; i >= 0; i--) { 26 | int result = decodeFlashGap(irReader.getDuration(2 * i + index), irReader.getDuration(2 * i + 1 + index)); 27 | if (result == invalid) 28 | return invalid; 29 | sum = (sum << 1) + result; 30 | } 31 | return sum; 32 | } 33 | 34 | Nec1Decoder::Nec1Decoder(const IrReader &irReader) : IrDecoder() { 35 | unsigned int index = 0; 36 | bool success; 37 | if (irReader.getDataLength() == 4U) { 38 | success = getDuration(irReader.getDuration(index++), 16U); 39 | if (!success) 40 | return; 41 | success = getDuration(irReader.getDuration(index++), 4U); 42 | if (!success) 43 | return; 44 | success = getDuration(irReader.getDuration(index++), 1U); 45 | if (!success) 46 | return; 47 | success = isEnding(irReader.getDuration(index)); 48 | if (!success) 49 | return; 50 | ditto = true; 51 | setValid(true); 52 | //strcpy_PF(decode, (uint_farptr_t) nec1DittoLiteral); // FIXME 53 | strcpy(decode, nec1DittoLiteral); // FIXME 54 | } else if (irReader.getDataLength() == 34U * 2U) { 55 | success = getDuration(irReader.getDuration(index++), 16U); 56 | if (!success) 57 | return; 58 | success = getDuration(irReader.getDuration(index++), 8U); 59 | if (!success) 60 | return; 61 | D = decodeParameter(irReader, index); 62 | if (D == invalid) 63 | return; 64 | index += 16; 65 | S = decodeParameter(irReader, index); 66 | if (S == invalid) 67 | return; 68 | index += 16; 69 | F = decodeParameter(irReader, index); 70 | if (F == invalid) 71 | return; 72 | index += 16; 73 | int invF = decodeParameter(irReader, index); 74 | if (invF < 0) 75 | return; 76 | if ((F ^ invF) != 0xFF) 77 | return; 78 | index += 16; 79 | 80 | success = getDuration(irReader.getDuration(index++), 1U); 81 | if (!success) 82 | return; 83 | success = isEnding(irReader.getDuration(index)); 84 | if (!success) 85 | return; 86 | ditto = false; 87 | setValid(true); 88 | //strncpy_PF(decode, pgm_read_byte(nec1DittoLiteral), 4); 89 | //strcpy_PF(decode, (uint_farptr_t) F("NEC1")); 90 | strcpy(decode, "NEC1"); 91 | char junk[5]; 92 | sprintf(junk, " %d", D); 93 | strcat(decode, junk); 94 | if (S != 255 - D) { 95 | sprintf(junk, " %d", S); 96 | strcat(decode, junk); 97 | } 98 | sprintf(junk, " %d", F); 99 | strcat(decode, junk); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/IrSignal.cpp: -------------------------------------------------------------------------------- 1 | #include "IrSignal.h" 2 | #include "IrSender.h" 3 | 4 | IrSignal::IrSignal(const IrSequence& intro_, const IrSequence& repeat_, const IrSequence& ending_, 5 | frequency_t frequency_, dutycycle_t dutyCycle_) 6 | : frequency(frequency_),dutyCycle(dutyCycle_),intro(intro_),repeat(repeat_),ending(ending_) { 7 | } 8 | 9 | IrSignal::IrSignal(IrSequence&& intro_, IrSequence&& repeat_, IrSequence&& ending_, 10 | frequency_t frequency_, dutycycle_t dutyCycle_) 11 | : frequency(frequency_),dutyCycle(dutyCycle_),intro(intro_),repeat(repeat_),ending(ending_) { 12 | } 13 | 14 | IrSignal::IrSignal(const IrSequence& intro_, const IrSequence& repeat_, 15 | frequency_t frequency_, dutycycle_t dutyCycle_) 16 | : frequency(frequency_), dutyCycle(dutyCycle_), intro(intro_), repeat(repeat_), ending() { 17 | } 18 | 19 | IrSignal::IrSignal(IrSequence&& intro_, IrSequence&& repeat_, 20 | frequency_t frequency_, dutycycle_t dutyCycle_) 21 | : frequency(frequency_), dutyCycle(dutyCycle_), intro(intro_), repeat(repeat_), ending() { 22 | } 23 | 24 | IrSignal::IrSignal(const microseconds_t *intro_, size_t introLength, 25 | const microseconds_t *repeat_, size_t repeatLength, 26 | const microseconds_t *ending_, size_t endingLength, 27 | frequency_t frequency_, dutycycle_t dutyCycle_) 28 | : frequency(frequency_), 29 | dutyCycle(dutyCycle_), 30 | intro(intro_, introLength), 31 | repeat(repeat_, repeatLength), 32 | ending(ending_, endingLength) { 33 | } 34 | 35 | IrSignal::IrSignal(const microseconds_t *intro_, size_t introLength, 36 | const microseconds_t *repeat_, size_t repeatLength, 37 | frequency_t frequency_, dutycycle_t dutyCycle_) 38 | : frequency(frequency_), 39 | dutyCycle(dutyCycle_), 40 | intro(intro_, introLength), 41 | repeat(repeat_, repeatLength), 42 | ending() { 43 | } 44 | 45 | #if HAS_FLASH_READ 46 | 47 | IrSignal* IrSignal::readFlash(const microseconds_t *intro, size_t lengthIntro, 48 | const microseconds_t *repeat, size_t lengthRepeat, 49 | const microseconds_t *ending, size_t lengthEnding, 50 | frequency_t frequency_, 51 | dutycycle_t dutyCycle_) { 52 | return new IrSignal(*IrSequence::readFlash(intro, lengthIntro), 53 | *IrSequence::readFlash(repeat, lengthRepeat), 54 | *IrSequence::readFlash(ending, lengthEnding), 55 | frequency_, dutyCycle_); 56 | } 57 | 58 | IrSignal* IrSignal::readFlash(const microseconds_t *intro, size_t lengthIntro, 59 | const microseconds_t *repeat, size_t lengthRepeat, 60 | frequency_t frequency_, 61 | dutycycle_t dutyCycle_) { 62 | return new IrSignal(*IrSequence::readFlash(intro, lengthIntro), 63 | *IrSequence::readFlash(repeat, lengthRepeat), 64 | frequency_, dutyCycle_); 65 | } 66 | #endif 67 | 68 | void IrSignal::dump(Stream& stream, bool usingSigns) const { 69 | bool printedSomething = dumpFrequency(stream); 70 | if (printedSomething) 71 | stream.print(" "); 72 | printedSomething = dumpDutyCycle(stream); 73 | if (printedSomething) 74 | stream.print(" "); 75 | intro.dump(stream, usingSigns); 76 | repeat.dump(stream, usingSigns); 77 | ending.dump(stream, usingSigns); 78 | } 79 | 80 | bool IrSignal::dumpFrequency(Stream& stream, frequency_t frequency) { 81 | if (frequency > 0 && frequency != invalidFrequency) { 82 | stream.print(F("f=")); 83 | stream.print(frequency); 84 | return true; 85 | } 86 | return false; 87 | } 88 | 89 | bool IrSignal::dumpDutyCycle(Stream& stream, dutycycle_t dutyCycle) { 90 | if (dutyCycle > 0 && dutyCycle < 100) { 91 | stream.print(dutyCycle); 92 | stream.print("%"); 93 | return true; 94 | } 95 | return false; 96 | } -------------------------------------------------------------------------------- /src/boards/Teensy3x.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | /** 19 | * @file Teensy3x.h 20 | * 21 | * @brief Hardware dependent definitions for Teensy 3.1/3.2 and probably more. 22 | * Based upon boarddefs.h from IRremote. 23 | * Likely originally written by Paul Stoffregen. Somewhat modified. 24 | */ 25 | 26 | #pragma once 27 | 28 | #define CURRENT_CLASS Teensy3x 29 | 30 | // https://www.pjrc.com/teensy/troubleshoot.html says: 31 | // Teensy LC, 3.0, 3.1 do not have this problem. 32 | // Variables defined with "const" are placed only in flash memory, but can be accessed normally. 33 | // Only the older 8 bit AVR-based boards require these special steps to prevent strings 34 | // and read-only variables from consuming limited RAM. 35 | #define HAS_FLASH_READ 0 36 | #define HAS_HARDWARE_PWM 1 37 | #define HAS_SAMPLING 1 38 | #define HAS_INPUT_CAPTURE 0 39 | 40 | #define TIMER_INTR_NAME cmt_isr 41 | #ifndef LED_BUILTIN 42 | #define LED_BUILTIN 13 43 | #endif 44 | 45 | #ifdef ISR 46 | #undef ISR 47 | #endif 48 | #define ISR(f) void f(void) 49 | 50 | #if F_BUS < 8000000 51 | #error IRremote requires at least 8 MHz on Teensy 3.x 52 | #endif 53 | 54 | class Teensy3x : public Board { 55 | public: 56 | 57 | Teensy3x() { 58 | }; 59 | 60 | // not tested yet 61 | void reset() { 62 | // https://forum.pjrc.com/threads/52512-External-RESET-button-Teensy-3-2?p=180363&viewfull=1#post180363 63 | SCB_AIRCR = 0x05FA0004; 64 | } 65 | 66 | private: 67 | 68 | void timerReset() { 69 | uint8_t tmp __attribute__((unused)) = CMT_MSC; 70 | CMT_CMD2 = 30U; 71 | }; 72 | 73 | void timerEnablePwm() { 74 | CORE_PIN5_CONFIG = PORT_PCR_MUX(2) | PORT_PCR_DSE | PORT_PCR_SRE; 75 | }; 76 | 77 | void timerDisablePwm() { 78 | CORE_PIN5_CONFIG = PORT_PCR_MUX(1) | PORT_PCR_DSE | PORT_PCR_SRE; 79 | }; 80 | 81 | void timerEnableIntr() { 82 | NVIC_ENABLE_IRQ(IRQ_CMT); 83 | }; 84 | 85 | void timerDisableIntr() { 86 | NVIC_DISABLE_IRQ(IRQ_CMT); 87 | }; 88 | 89 | static const uint32_t CMT_PPS_DIV = (F_BUS + 7999999U) / 8000000U; // = 5 90 | 91 | void timerConfigHz(frequency_t frequency, dutycycle_t dutyCycle) { 92 | SIM_SCGC4 |= SIM_SCGC4_CMT; 93 | SIM_SOPT2 |= SIM_SOPT2_PTD7PAD; 94 | 95 | CMT_PPS = CMT_PPS_DIV - 1U; 96 | CMT_CGH1 = F_BUS / CMT_PPS_DIV / frequency * dutyCycle / 100U; 97 | CMT_CGL1 = F_BUS / CMT_PPS_DIV / frequency * (100 - dutyCycle) / 100U; 98 | CMT_CMD1 = 0U; 99 | CMT_CMD2 = 30U; 100 | CMT_CMD3 = 0U; 101 | CMT_CMD4 = 0U; 102 | CMT_OC = 0x60U; 103 | CMT_MSC = 0x01U; 104 | }; 105 | 106 | void timerConfigNormal() { 107 | SIM_SCGC4 |= SIM_SCGC4_CMT; 108 | CMT_PPS = CMT_PPS_DIV - 1U; 109 | CMT_CGH1 = 1U; 110 | CMT_CGL1 = 1U; 111 | CMT_CMD1 = 0U; 112 | CMT_CMD2 = 30U; 113 | CMT_CMD3 = 0U; 114 | CMT_CMD4 = (F_BUS / 160000U + CMT_PPS_DIV / 2U) / CMT_PPS_DIV - 31U; 115 | CMT_OC = 0U; 116 | CMT_MSC = 0x03U; 117 | }; 118 | 119 | #define PWM_PIN 5 120 | }; -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for creating stuff on host. 2 | # Use Arduino IDE for compiling for Arduino. 3 | 4 | # The functions for generating keywords.txt requires KeywordsTxtGenerator, 5 | # https://github.com/bengtmartensson/KeywordsTxtGenerator, to be installed in 6 | # KEYWORD_TXT_GENERATOR_DIR. 7 | 8 | ifneq ($(ARDUINO),) 9 | $(error This Makefile is not for compiling target code, for that, use the Arduino IDE.) 10 | endif 11 | 12 | KEYWORD_TXT_GENERATOR_DIR = ../KeywordsTxtGenerator 13 | DOXYGEN := doxygen 14 | DOXYFILE := Doxyfile 15 | XSLTPROC := xsltproc 16 | TRANSFORMATION := $(KEYWORD_TXT_GENERATOR_DIR)/doxygen2keywords.xsl 17 | 18 | CXX:=g++ 19 | BROWSER:=firefox 20 | DEBUGFLAGS:=-g 21 | WARNINGFLAGS:=-Wall -Werror -Wextra -pedantic 22 | 23 | VPATH=tests src src/boards 24 | 25 | GH_PAGES := gh-pages 26 | VERSION_H := src/version.h 27 | 28 | # Get VERSION from the version in library.properties 29 | VERSION := $(subst version=,,$(shell grep version= library.properties)) 30 | 31 | ORIGINURL := $(shell git remote get-url origin) 32 | 33 | .PRECIOUS: test1 34 | 35 | OBJS=\ 36 | Board.o \ 37 | HashDecoder.o \ 38 | IrReader.o \ 39 | IrReceiver.o \ 40 | IrReceiverPoll.o \ 41 | IrReceiverSampler.o \ 42 | IrSender.o \ 43 | IrSender.o \ 44 | IrSenderNonMod.o \ 45 | IrSenderPwm.o \ 46 | IrSenderPwmHard.o \ 47 | IrSenderPwmSoft.o \ 48 | IrSenderPwmSoftDelay.o \ 49 | IrSenderPwmSpinWait.o \ 50 | IrSenderSimulator.o \ 51 | IrSequence.o \ 52 | IrSignal.o \ 53 | IrWidget.o \ 54 | IrWidgetAggregating.o \ 55 | MultiDecoder.o \ 56 | Nec1Decoder.o \ 57 | Nec1Renderer.o \ 58 | Pronto.o \ 59 | Rc5Decoder.o \ 60 | Rc5Renderer.o \ 61 | SIL.o 62 | 63 | EXTRA_INCLUDES=\ 64 | InfraredTypes.h \ 65 | IrDecoder.h \ 66 | IrSenderNonMod.h \ 67 | IrSequenceReader.h \ 68 | 69 | NOT_EXPORTED_INCLUDE = SIL.h Board.h 70 | 71 | EXPORTED_INCLUDES := $(sort $(filter-out $(NOT_EXPORTED_INCLUDE), $(EXTRA_INCLUDES) $(subst .o,.h,$(OBJS)))) 72 | 73 | all: test doc keywords.txt library.properties 74 | 75 | libInfrared.a: $(OBJS) 76 | $(AR) rs $@ $(OBJS) 77 | 78 | %.o: %.cpp 79 | $(CXX) -Isrc -std=c++11 $(WARNINGFLAGS) $(OPTIMIZEFLAGS) $(DEBUGFLAGS) -c $< 80 | 81 | test%: test%.o libInfrared.a 82 | $(CXX) -o $@ $< -L. -lInfrared 83 | ./$@ 84 | 85 | release: push gh-pages tag deploy 86 | 87 | push: 88 | git push 89 | 90 | deploy: 91 | 92 | version: $(VERSION_H) 93 | 94 | $(VERSION_H): library.properties Makefile 95 | echo "Creating $(VERSION_H)" 96 | @echo "// This file was automatically generated from $<; do not edit." > $@ 97 | @echo "#pragma once" >> $@ 98 | @echo "/**" >> $@ 99 | @echo " * Version of the current library." >> $@ 100 | @echo " * Taken from the version in $<." >> $@ 101 | @echo " */" >> $@ 102 | @echo "#define VERSION \"$(VERSION)\"" >> $@ 103 | 104 | api-doc/index.html xml/index.xml: $(wildcard src/*) $(VERSION_H) $(DOXYFILE) README.md 105 | GIT_VERSION=$(VERSION) $(DOXYGEN) $(DOXYFILE) 106 | 107 | doc: api-doc/index.html 108 | $(BROWSER) $< 109 | 110 | gh-pages: api-doc/index.html 111 | rm -rf $(GH_PAGES) 112 | git clone --depth 1 -b gh-pages ${ORIGINURL} ${GH_PAGES} 113 | ( cd ${GH_PAGES} ; \ 114 | cp -rf ../api-doc/* . ; \ 115 | git add * ; \ 116 | git commit -S -a -m "Update of API documentation" ; \ 117 | git push ) 118 | 119 | tag: 120 | git checkout master 121 | git status 122 | git tag -s -a Version-$(VERSION) -m "Tagging Version-$(VERSION)" 123 | git push origin Version-$(VERSION) 124 | 125 | clean: 126 | rm -rf *.a *.o api-doc xml test1 $(GH_PAGES) library.properties.tmp 127 | 128 | spotless: clean 129 | rm -rf keywords.txt 130 | 131 | build-tests: 132 | 133 | test: test1 134 | 135 | keywords.txt: xml/index.xml 136 | $(XSLTPROC) $(TRANSFORMATION) $< > $@ 137 | 138 | library.properties: Makefile 139 | sed -e "s/^includes=.*/includes=$(EXPORTED_INCLUDES:%=%,)/" -e s/,$$// $@ > $@.tmp 140 | mv $@.tmp $@ 141 | 142 | .PHONY: clean spotless doc 143 | -------------------------------------------------------------------------------- /src/IrWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "IrSignal.h" 2 | #include "Board.h" // for HAS_INPUT_CAPTURE 3 | 4 | #if HAS_INPUT_CAPTURE 5 | 6 | /* IR Widget: capture a raw IR signal and dump the timing of the non-demodulated signal 7 | 8 | http://www.piclist.com/images/boards/irwidget/index.htm 9 | http://www.hifi-remote.com/forums/dload.php?action=file&file_id=2044 10 | http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide 11 | http://www.compendiumarcana.com/irwidget/ 12 | 13 | Arduino digital pin numbers for the input capture pin (ICP) and the logic analyzer debugging pin (LA Dbg): 14 | Board name / MCU | ICP pin | LA Dbg pin 15 | -------------------------------------------|--------------.-----------|------------------------ 16 | Duemilanove/Uno (ATmega328P / ATmega168) | ICP1/PB0, Arduino pin 8 | PD6, Arduino pin 6 17 | Leonardo (ATmega32U4) | ICP1/PD4, Arduino pin 4 | PD6, Arduino pin 12 18 | Arduino Mega 2560 (ATmega2560) | ICP4/PL0, Arduino pin 49 | PL6, Arduino pin 43 19 | 20 | see also here: 21 | http://arduino.cc/en/Hacking/PinMapping168 (also for ATmega328P) 22 | http://arduino.cc/en/Hacking/PinMapping32u4 23 | http://arduino.cc/en/Hacking/PinMapping2560 24 | */ 25 | 26 | // Copyright (c) 2012 Michael Dreher 27 | // this code may be distributed under the terms of the General Public License V2 (GPL V2) 28 | // NOTE(BM) Michael agrees to "or later", see 29 | // http://www.hifi-remote.com/forums/viewtopic.php?p=112586#112586 30 | 31 | // Code slighty reorganized by Bengt Martensson 32 | 33 | #include "IrWidget.h" 34 | 35 | IrWidget::IrWidget(size_t captureLength, 36 | bool pullup, 37 | int16_t markExcess, 38 | milliseconds_t beginningTimeout, 39 | milliseconds_t endingTimeout) : IrReader(captureLength) { 40 | setup(pullup); 41 | captureData = new microseconds_t[bufferSize]; 42 | setMarkExcess(markExcess); 43 | setBeginningTimeout(beginningTimeout); 44 | //endingTimeout = _BV(RANGE_EXTENSION_BITS) - 1; 45 | setEndingTimeout(endingTimeout); 46 | 47 | // Test that allocated memory is indeed usable. Otherwise crash will occur. 48 | for (unsigned int i = 0; i < bufferSize; i++) 49 | captureData[i] = 0; 50 | } 51 | 52 | IrWidget::~IrWidget() { 53 | delete[] captureData; 54 | } 55 | 56 | void IrWidget::setEndingTimeout(milliseconds_t timeOut) { 57 | endingTimeout = (ovlBitsDataType) ((timeOut/* /1000*/ + 16)/32); 58 | } 59 | 60 | milliseconds_t IrWidget::getEndingTimeout() const { 61 | //return (uint16_t) (timerValueToNanoSeconds((((uint32_t) endingTimeout)*CAPTURE_PRESCALER_FACTOR)) / 1000); 62 | return (uint16_t) 1000 * (endingTimeout << 15); 63 | } 64 | 65 | void IrWidget::dump(Stream &stream) const { 66 | bool printedSomething = IrSignal::dumpFrequency(stream, getFrequency()); 67 | if (printedSomething) 68 | stream.print(' '); 69 | IrReader::dump(stream); 70 | } 71 | 72 | //////////////////////////////////////////////////////////////////////////////// 73 | // Initialization 74 | //////////////////////////////////////////////////////////////////////////////// 75 | 76 | // initialize Timer and IO pins, needs to be called once 77 | void IrWidget::setup(bool pullup) { 78 | #ifdef ARDUINO 79 | // configure signal capture ICP pin as input 80 | cbi(CAT2(DDR, CAP_PORT), CAP_PIN); 81 | if (pullup) 82 | sbi(CAT2(PORT, CAP_PORT), CAP_PIN); // enable the internal 10k pull-up resistor 83 | 84 | #if defined(DEBUG_PIN) && defined(DEBUG_PORT) 85 | sbi(CAT2(DDR, DEBUG_PORT), DEBUG_PIN); // configure logic analyzer debug pin as output 86 | #endif 87 | 88 | // init timer, disable power save mode of timer 89 | #ifdef PRR0 // for ATmega32U4 and ATmega2560 90 | #if PRTIM <= 2 91 | cbi(PRR0, CAT2(PRTIM, CAP_TIM)); // for ATmega32U4 and ATmega2560 92 | #else 93 | cbi(PRR1, CAT2(PRTIM, CAP_TIM)); // for ATmega2560 94 | #endif 95 | #else 96 | cbi(PRR, CAT2(PRTIM, CAP_TIM)); 97 | #endif 98 | 99 | CAT3(TCCR, CAP_TIM, A) = 0; // Timer mode 0 = normal 100 | CAT3(TCCR, CAP_TIM, B) = _BV(CAT2(ICNC, CAP_TIM)) | CAPTURE_PRESCALER_SETTING; // prescaler according to setting, enable noise canceler 101 | #else 102 | std::cout << "pinMode(CAPTURE_PIN_1, " << (pullup ? "INPUT_PULLUP)" : "INPUT)") << std::endl; 103 | #endif 104 | } 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /src/boards/ATmega32U4.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | /** 19 | * @file ATmega32U4.h 20 | * 21 | * @brief Hardware dependent definitions for Leonardo like boards like 22 | * Leonardo and Micro. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "avr.h" 28 | 29 | #if !defined(ARDUINO_AVR_LEONARDO) && !defined(ARDUINO_AVR_MICRO) 30 | #error Board not supported 31 | #endif 32 | 33 | #define CURRENT_CLASS ATmega32U4 34 | 35 | class ATmega32U4 : public Board { 36 | public: 37 | ATmega32U4() {}; 38 | 39 | private: 40 | 41 | /////////////////////////////////////////////////////////////////////////////// 42 | #if defined(IR_USE_TIMER1) 43 | // Timer1 (16 bits) 44 | #define TIMER_INTR_NAME TIMER1_COMPA_vect 45 | 46 | void timerEnablePwm() { 47 | TCCR1A |= _BV(COM1A1); 48 | }; 49 | 50 | void timerDisablePwm() { 51 | TCCR1A &= ~(_BV(COM1A1)); 52 | }; 53 | 54 | void timerEnableIntr() { 55 | TIMSK1 = _BV(OCIE1A); 56 | }; 57 | 58 | void timerDisableIntr() { 59 | TIMSK1 = 0U; 60 | }; 61 | 62 | void timerConfigHz(frequency_t frequency, dutycycle_t dutyCycle) { 63 | const uint16_t pwmval = F_CPU / 2UL / frequency; 64 | TCCR1A = _BV(WGM11); 65 | TCCR1B = _BV(WGM13) | _BV(CS10); 66 | ICR1 = pwmval; 67 | OCR1A = pwmval * dutyCycle / 100UL; 68 | }; 69 | 70 | void timerConfigNormal() { 71 | TCCR1A = 0; 72 | TCCR1B = _BV(WGM12) | _BV(CS10); 73 | OCR1A = F_CPU * microsPerTick / 1000000UL; 74 | TCNT1 = 0U; 75 | }; 76 | 77 | #define PWM_PIN 9 78 | 79 | /////////////////////////////////////////////////////////////////////////////// 80 | #elif defined(IR_USE_TIMER2) // ! defined(IR_USE_TIMER1) 81 | #define TIMER_INTR_NAME TIMER2_COMPA_vect 82 | 83 | #error IR_USE_TIMER2 in Leonardo currently broken. 84 | 85 | void timerEnablePwm() {TCCR2A |= _BV(COM2B1);}; 86 | void timerDisablePwm() { TCCR2A &= ~(_BV(COM2B1));}; 87 | void timerEnableIntr() {TIMSK2 = _BV(OCIE2A);}; 88 | void timerDisableIntr() {TIMSK2 = 0;}; 89 | 90 | void timerConfigHz(frequency_t frequency, dutycycle_t dutyCycle) { 91 | const uint8_t pwmval = F_CPU / 2U / frequency; 92 | TCCR2A = _BV(WGM20); 93 | TCCR2B = _BV(WGM22) | _BV(CS20); 94 | OCR2A = pwmval; 95 | OCR2B = pwmval * dutyCycle / 100UL; \ 96 | }) 97 | 98 | #define TIMER_COUNT_TOP (F_CPU * microsPerTick / 1000000) 99 | 100 | void timerConfigNormal() { 101 | TCCR2A = _BV(WGM21); 102 | TCCR2B = _BV(CS21); 103 | OCR2A = TIMER_COUNT_TOP / 8U; 104 | TCNT2 = 0U; 105 | }; 106 | 107 | #define PWM_PIN 3 108 | 109 | #else // ! defined(IR_USE_TIMER2) 110 | 111 | #error Config error, either IR_USE_TIMER1 or IR_USE_TIMER2 must be defined. 112 | 113 | #endif 114 | }; 115 | 116 | /* http://busyducks.com/ascii-art-arduinos 117 | 118 | D0 D1 RST 119 | GND GND VCC RX TX /DTR 120 | +--------------------------------+ 121 | | [ ] [ ] [ ] [ ] [ ] [ ] | 122 | | FTDI | 123 | D1 | [ ]1/TX RAW[ ] | 124 | D0 | [ ]0/RX GND[ ] | 125 | | [ ]RST SCL/A5[ ] RST[ ] | C6 126 | | [ ]GND SDA/A4[ ] VCC[ ] | 127 | D2 | [ ]2/INT0 ___ A3[ ] | C3 128 | D3 |~[ ]3/INT1 / \ A2[ ] | C2 129 | D4 | [ ]4 /PRO \ A1[ ] | C1 130 | D5 |~[ ]5 \ MINI/ A0[ ] | C0 131 | D6 |~[ ]6 \___/ SCK/13[ ] | B5 132 | D7 | [ ]7 A7[ ] MISO/12[ ] | B4 133 | B0 | [ ]8 A6[ ] MOSI/11[ ]~| B3 134 | B1 |~[ ]9 SS/10[ ]~| B2 135 | | [RST-BTN] | 136 | +--------------------------------+ 137 | */ -------------------------------------------------------------------------------- /src/Arduino.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef ARDUINO 4 | #include_next 5 | 6 | #else // ! ARDUINO 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "InfraredTypes.h" 16 | #include "PinModeStatus.h" 17 | 18 | typedef void *uint_farptr_t; 19 | typedef void __FlashStringHelper; 20 | 21 | #define REPORT_TIMES 22 | //#define REAL_TIME 23 | 24 | #define String std::string 25 | 26 | #define indexOf find 27 | #define charAt at 28 | #define substring substr 29 | 30 | #define F(x) x 31 | 32 | #define A0 100 33 | #define A1 101 34 | #define A2 102 35 | #define A3 103 36 | #define A4 104 37 | #define A5 101 38 | #define A6 106 39 | #define A7 107 40 | #define LED_BUILTIN 13 41 | 42 | // Can't use pin_t yet 43 | extern uint8_t currentWritePin; // SIL.cpp 44 | extern struct timeval simulatedTime; 45 | 46 | static timeval getTimeOfDay() { 47 | #ifdef REAL_TIME 48 | struct timeval tv; 49 | gettimeofday(&tv, NULL); 50 | return tv; 51 | #else 52 | return simulatedTime; 53 | #endif 54 | } 55 | 56 | static struct timeval last = getTimeOfDay(); 57 | static PinStatus lastValue = LOW; 58 | 59 | inline void pinMode(uint8_t pin, PinMode mode) { 60 | std::cout << "pinMode(" << (int) pin << ", " 61 | << (mode == INPUT ? "INPUT" : mode == OUTPUT ? "OUTPUT" : mode == INPUT_PULLUP ? "INPUT_PULLUP" : "INPUT_PULLDOWN") 62 | << ")" << std::endl; 63 | if (mode == OUTPUT) { 64 | currentWritePin = pin; 65 | lastValue = LOW; 66 | } 67 | } 68 | 69 | inline void delayMicroseconds(unsigned int t) { 70 | #ifdef REAL_TIME 71 | usleep(t); 72 | #else 73 | simulatedTime.tv_sec += t/1000000U; 74 | simulatedTime.tv_usec += t % 1000000U; 75 | while (simulatedTime.tv_usec > 1000000U) { 76 | simulatedTime.tv_usec -= 1000000U; 77 | simulatedTime.tv_sec++; 78 | } 79 | #endif 80 | } 81 | 82 | inline void delay(unsigned long t) { 83 | delayMicroseconds(1000U * t); 84 | } 85 | 86 | inline void noInterrupts() {} 87 | inline void interrupts() {} 88 | inline void yield() {} 89 | 90 | inline unsigned long micros() { 91 | struct timeval tv = getTimeOfDay(); 92 | // Probably overflows, but this should be OK in 99.99% of all cases, which is enough here. 93 | return 1000000UL * tv.tv_sec + tv.tv_usec; 94 | } 95 | 96 | inline unsigned long millis() { 97 | struct timeval tv; 98 | gettimeofday(&tv, NULL); 99 | // Probably overflows, but this should be OK in 99.99% of all cases, which is enough here. 100 | return 1000*tv.tv_sec + tv.tv_usec/1000; 101 | } 102 | 103 | inline uint8_t digitalRead(uint8_t pin __attribute__((unused))) { 104 | return 0; 105 | } 106 | 107 | inline void digitalWrite(uint8_t pin __attribute__((unused)), PinStatus value) { 108 | #ifdef REPORT_TIMES 109 | // if (pin != currentPin) 110 | // return; 111 | struct timeval now = getTimeOfDay(); 112 | if (lastValue == -1) 113 | if (value == LOW) 114 | return; 115 | else { 116 | lastValue = value; 117 | last = now; 118 | } 119 | else if (lastValue == (value != LOW)) 120 | return; 121 | else { 122 | char sign = value ? '-' : '+'; 123 | std::cout << sign << 1000000UL * (now.tv_sec - last.tv_sec) + (now.tv_usec - last.tv_usec) << " "; 124 | last = now; 125 | lastValue = value; 126 | } 127 | #else 128 | std::cout << "digitalWrite(" << (int) pin << ", " 129 | << (value == LOW ? "LOW" : "HIGH") << ")" << std::endl; 130 | #endif 131 | } 132 | 133 | 134 | #define F_CPU 16000000 // Good default, correct for Unu etc 135 | 136 | // Stream 137 | 138 | class Stream { 139 | private: 140 | std::ostream& stream; 141 | public: 142 | Stream(std::ostream& stream_) : stream(stream_) {}; 143 | void print(char c) { stream << c; }; 144 | void print(const char *c) { stream << c; }; 145 | void print(const std::string& string) { stream << string; }; 146 | void print(int x) { stream << x; }; 147 | void print(uint32_t x) { stream << x; }; 148 | void print(int x, std::ios_base& manipulator(std::ios_base&)) { stream << manipulator << x; }; 149 | void println() { stream << std::endl; }; 150 | void println(const std::string& string) { stream << string << std::endl; }; 151 | void println(long x) { stream << x << std::endl; } 152 | int available() { return 0; } 153 | }; 154 | 155 | #define DEC std::dec 156 | 157 | #endif // ! ARDUINO 158 | -------------------------------------------------------------------------------- /src/IrReceiverSampler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IrReceiver.h" 4 | 5 | /** 6 | * @class IrReceiverSampler 7 | * This receiving class samples the input pin every 50 microseconds using a timer 8 | * interrupt. Due to the ISP, this is a singleton class; 9 | * it can only be instantiated once. 10 | * This is enforced by the absence of public constructors: 11 | * it has to be instantiated by the 12 | * factory method newIrReceiverSampler. 13 | */ 14 | 15 | // The interrupt routine must have access to some stuff here. 16 | // This needs to be public, and, since the interrupt routing 17 | // fumbles with it, volatile. 18 | class IrReceiverSampler : public IrReceiver { 19 | public: 20 | 21 | /** State space for the receiver state machine. */ 22 | enum ReceiverState_t { 23 | STATE_IDLE, /**< Between signals; waiting for first mark */ 24 | STATE_MARK, /**< Last read a mark */ 25 | STATE_SPACE, /**< Last read a space */ 26 | STATE_STOP /**< Complete signal has been read */ 27 | }; 28 | 29 | /** State of the state machine */ 30 | volatile ReceiverState_t receiverState; // previously rcvstate; 31 | 32 | // Note: use the following ones instead of beginningTimeout and endingTimeout 33 | // inherited from IrReader. 34 | uint32_t endingTimeoutInTicks; // previously GAP_TICKS 35 | 36 | uint32_t beginningTimeoutInTicks; // previously TIMEOUT_TICKS; 37 | 38 | /** state timer, counts 50uS ticks. */ 39 | volatile uint32_t timer; 40 | 41 | /** Data buffer */ 42 | volatile microseconds_t *durationData; // previously rawbuf; 43 | 44 | /** Number of entries in durationData */ 45 | volatile size_t dataLength; // previously rawlen 46 | 47 | private: 48 | static IrReceiverSampler *instance; 49 | static uint32_t millisecs2ticks(milliseconds_t ms); 50 | static milliseconds_t ticks2millisecs(uint32_t tix); 51 | 52 | protected: 53 | virtual ~IrReceiverSampler(); 54 | 55 | private: 56 | IrReceiverSampler(size_t captureLength = defaultCaptureLength, 57 | pin_t pin = defaultPin, 58 | bool pullup = false, 59 | microseconds_t markExcess = defaultMarkExcess, 60 | milliseconds_t beginningTimeout = defaultBeginningTimeout, 61 | milliseconds_t endingTimeout = defaultEndingTimeout); 62 | 63 | public: 64 | /** 65 | * This factory method replaces public constructors. Provided that no instance currently exists, 66 | * it constructs a new instance and return a pointer to it, if possible. Otherwise, it returns nullptr. 67 | * 68 | * @param captureLength buffersize requested 69 | * @param pin GPIO pin to use 70 | * @param pullup true if the internal pullup resistor should be enabled 71 | * @param markExcess markExcess to use 72 | * @param beginningTimeout beginningTimeout to use 73 | * @param endingTimeout endingTimeout to use 74 | * @return pointer to a valid instance, or nullptr. 75 | */ 76 | static IrReceiverSampler *newIrReceiverSampler(size_t captureLength = defaultCaptureLength, 77 | pin_t pin = defaultPin, 78 | bool pullup = false, 79 | microseconds_t markExcess = defaultMarkExcess, 80 | milliseconds_t beginningTimeout = defaultBeginningTimeout, 81 | milliseconds_t endingTimeout = defaultEndingTimeout); 82 | 83 | /** 84 | * Deletes the instance, thereby freeing up the resources it occupied, and 85 | * allowing for another instance to be created. 86 | */ 87 | static void deleteInstance(); 88 | 89 | /** 90 | * Returns a pointer to the instance, or nullptr. 91 | * @return pointer to instance, possibly nullptr. 92 | */ 93 | static IrReceiverSampler *getInstance() { 94 | return instance; 95 | } 96 | 97 | void enable(); 98 | 99 | void disable(); 100 | 101 | void reset(); 102 | 103 | void setEndingTimeout(milliseconds_t timeOut); 104 | 105 | milliseconds_t getEndingTimeout() const; 106 | 107 | void setBeginningTimeout(milliseconds_t timeOut); 108 | 109 | milliseconds_t getBeginningTimeout() const; 110 | 111 | size_t getDataLength() const { 112 | return dataLength; 113 | } 114 | 115 | microseconds_t getDuration(unsigned int i) const { 116 | uint32_t bigvalue = Board::microsPerTick * static_cast(durationData[i]) + (i & 1 ? markExcess : -markExcess); 117 | return bigvalue <= MICROSECONDS_T_MAX ? (microseconds_t) bigvalue : MICROSECONDS_T_MAX; 118 | } 119 | 120 | bool isReady() const { 121 | return receiverState == STATE_STOP; 122 | } 123 | }; 124 | -------------------------------------------------------------------------------- /src/IrReceiverSampler.cpp: -------------------------------------------------------------------------------- 1 | #include "IrReceiverSampler.h" 2 | #include "Board.h" 3 | 4 | #if HAS_SAMPLING 5 | 6 | uint32_t IrReceiverSampler::millisecs2ticks(milliseconds_t ms) { 7 | return (1000UL * (uint32_t) ms) / Board::microsPerTick; 8 | } 9 | 10 | milliseconds_t IrReceiverSampler::ticks2millisecs(uint32_t tix) { 11 | return (milliseconds_t) ((tix * Board::microsPerTick)/1000UL); 12 | } 13 | 14 | IrReceiverSampler *IrReceiverSampler::instance = nullptr; 15 | 16 | IrReceiverSampler::IrReceiverSampler(size_t captureLength, 17 | pin_t pin_, 18 | bool pullup, 19 | microseconds_t markExcess, 20 | milliseconds_t beginningTimeout, 21 | milliseconds_t endingTimeout) : IrReceiver(captureLength, pin_, pullup, markExcess) { 22 | setBeginningTimeout(beginningTimeout); 23 | setEndingTimeout(endingTimeout); 24 | durationData = new microseconds_t[bufferSize]; 25 | dataLength = 0; 26 | timer = 0; 27 | receiverState = STATE_IDLE; 28 | } 29 | 30 | IrReceiverSampler *IrReceiverSampler::newIrReceiverSampler(size_t captureLength, 31 | pin_t pin, 32 | bool pullup, 33 | microseconds_t markExcess, 34 | milliseconds_t beginningTimeout, 35 | milliseconds_t endingTimeout) { 36 | if (instance != nullptr || pin == invalidPin) 37 | return nullptr; 38 | instance = new IrReceiverSampler(captureLength, pin, pullup, markExcess, beginningTimeout, endingTimeout); 39 | return instance; 40 | } 41 | 42 | void IrReceiverSampler::deleteInstance() { 43 | delete instance; 44 | instance = nullptr; 45 | } 46 | 47 | IrReceiverSampler::~IrReceiverSampler() { 48 | delete [] durationData; 49 | } 50 | 51 | /* 52 | * The original IRrecv which uses 50us timer driven interrupts to sample input pin. 53 | */ 54 | void IrReceiverSampler::reset() { 55 | receiverState = STATE_IDLE; 56 | dataLength = 0; 57 | timer = 0U; 58 | } 59 | 60 | void IrReceiverSampler::enable() { 61 | // Initialize state machine variables 62 | reset(); 63 | noInterrupts(); 64 | Board::getInstance()->enableSampler(getPin()); 65 | interrupts(); 66 | } 67 | 68 | void IrReceiverSampler::disable() { 69 | Board::getInstance()->disableSampler(); 70 | } 71 | 72 | void IrReceiverSampler::setEndingTimeout(milliseconds_t timeOut) { 73 | endingTimeoutInTicks = millisecs2ticks(timeOut); 74 | } 75 | 76 | void IrReceiverSampler::setBeginningTimeout(milliseconds_t timeOut) { 77 | beginningTimeoutInTicks = millisecs2ticks(timeOut); 78 | } 79 | 80 | milliseconds_t IrReceiverSampler::getEndingTimeout() const { 81 | return ticks2millisecs(endingTimeoutInTicks); 82 | } 83 | 84 | milliseconds_t IrReceiverSampler::getBeginningTimeout() const { 85 | return ticks2millisecs(beginningTimeoutInTicks); 86 | } 87 | 88 | #ifdef ISR 89 | /** Interrupt routine. It collects data into the data buffer. */ 90 | ISR(TIMER_INTR_NAME) { 91 | Board::debugPinHigh(); 92 | Board::getInstance()->timerReset(); 93 | IrReceiverSampler *recv = IrReceiverSampler::getInstance(); 94 | IrReceiver::irdata_t irdata = recv->readIr(); 95 | recv->timer++; // One more 50us tick 96 | if (recv->dataLength >= recv->getBufferSize()) { 97 | // Buffer full 98 | recv->receiverState = IrReceiverSampler::STATE_STOP; 99 | } 100 | switch (recv->receiverState) { 101 | case IrReceiverSampler::STATE_IDLE: // Looking for first mark 102 | if (irdata == IrReceiver::IR_MARK) { 103 | // Got the first mark, record duration and start recording transmission 104 | recv->dataLength = 0; 105 | recv->timer = 0; 106 | recv->receiverState = IrReceiverSampler::STATE_MARK; 107 | } else { 108 | if (recv->timer >= recv->beginningTimeoutInTicks) { 109 | recv->durationData[recv->dataLength] = recv->timer; 110 | recv->timer = 0; 111 | recv->receiverState = IrReceiverSampler::STATE_STOP; 112 | } 113 | } 114 | break; 115 | case IrReceiverSampler::STATE_MARK: 116 | if (irdata == IrReceiver::IR_SPACE) { 117 | // MARK ended, record time 118 | recv->durationData[recv->dataLength++] = recv->timer; 119 | recv->timer = 0; 120 | recv->receiverState = IrReceiverSampler::STATE_SPACE; 121 | } 122 | break; 123 | case IrReceiverSampler::STATE_SPACE: 124 | if (irdata == IrReceiver::IR_MARK) { 125 | // SPACE just ended, record it 126 | recv->durationData[recv->dataLength++] = recv->timer; 127 | recv->timer = 0; 128 | recv->receiverState = IrReceiverSampler::STATE_MARK; 129 | } else { 130 | // still silence, is it over? 131 | if (recv->timer > recv->endingTimeoutInTicks) { 132 | // big SPACE, indicates gap between codes 133 | recv->durationData[recv->dataLength++] = recv->timer; 134 | // recv->timer = 0; 135 | recv->receiverState = IrReceiverSampler::STATE_STOP; 136 | } 137 | } 138 | break; 139 | case IrReceiverSampler::STATE_STOP: 140 | break; 141 | default: 142 | // should not happen 143 | break; 144 | } 145 | Board::debugPinLow(); 146 | } 147 | #endif // ISR 148 | 149 | #endif -------------------------------------------------------------------------------- /src/IrReader.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2015 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 2 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | #include "InfraredTypes.h" 23 | #include "IrSequence.h" 24 | 25 | /** 26 | * Abstract base class for all IR readers, capturing or receiving. 27 | * It should also serve as an interface description, 28 | * allowing for received data to be printed and decoded. 29 | */ 30 | class IrReader { 31 | public: 32 | // Defaults 33 | static constexpr milliseconds_t defaultBeginningTimeout = 2000U; 34 | static constexpr milliseconds_t defaultEndingTimeout = 30U; 35 | static constexpr size_t defaultCaptureLength = 100U; 36 | 37 | protected: 38 | milliseconds_t beginningTimeout; 39 | milliseconds_t endingTimeout; 40 | 41 | size_t bufferSize; 42 | 43 | /** Microseconds subtracted from pulses and added to gaps. May be negative. */ 44 | int16_t markExcess; 45 | 46 | /** True if last receive ended with a timeout */ 47 | bool timeouted; 48 | 49 | static unsigned int forceEven(unsigned int x) { 50 | return (x & 1) ? x + 1 : x; 51 | } 52 | 53 | /** 54 | * Constructs an IrReader with buffersize bufSize_, possibly increased to be even. 55 | * @param bufSize_ 56 | */ 57 | IrReader(size_t bufSize_) : bufferSize(forceEven(bufSize_)),timeouted(false) { 58 | } 59 | 60 | IrReader() { 61 | } 62 | 63 | #ifndef DOXYGEN 64 | IrReader(const IrReader&) = delete; 65 | IrReader(IrReader&&) = delete; 66 | IrReader& operator=(const IrReader&) = delete; 67 | IrReader& operator=(IrReader&&) = delete; 68 | #endif // ! DOXYGEN 69 | 70 | virtual ~IrReader() { 71 | }; 72 | 73 | public: 74 | virtual void reset() { 75 | timeouted = false; 76 | }; 77 | 78 | /** 79 | * Returns frequency of received signal. 80 | * This is a dumb default implementation, to be overridden when meaningful. 81 | * @return frequency 82 | */ 83 | virtual frequency_t getFrequency() const = 0; 84 | 85 | /** 86 | * Start reception of IR data. 87 | */ 88 | virtual void enable() { 89 | }; 90 | 91 | /** 92 | * Stop reception of IR data. 93 | */ 94 | virtual void disable() { 95 | }; 96 | 97 | /** 98 | * Convenience function: enable, wait until data is collected or timeout has occured, disable. 99 | */ 100 | virtual void receive() = 0; 101 | 102 | /** 103 | * Returns true if there is collected data. 104 | * @return status 105 | */ 106 | virtual bool isReady() const = 0; 107 | 108 | /** 109 | * Same as isReady() 110 | * @return 111 | */ 112 | operator bool() const { 113 | return isReady(); 114 | } 115 | 116 | /** 117 | * Returns the number of collected durations. 118 | * @return number durations 119 | */ 120 | virtual size_t getDataLength() const = 0; 121 | 122 | /** 123 | * Returns the index-th duration, if possible. 124 | * @param index index of duration 125 | * @return requested duration 126 | */ 127 | virtual microseconds_t getDuration(unsigned int index) const = 0; 128 | 129 | /** 130 | * Prints a textual representation of the received data to the Stream supplied. 131 | * @param stream Stream to which to print 132 | */ 133 | virtual void dump(Stream &stream) const; 134 | 135 | /** 136 | * Generates an IrSequence from the IrReader. 137 | * @return IrSequence. The user must delete this to avoid memory leaks. 138 | */ 139 | IrSequence *toIrSequence() const; 140 | 141 | virtual bool isEmpty() const { 142 | return getDataLength() == 0; 143 | } 144 | 145 | virtual void setEndingTimeout(milliseconds_t timeOut) { 146 | endingTimeout = timeOut; 147 | } 148 | 149 | virtual milliseconds_t getEndingTimeout() const { 150 | return endingTimeout; 151 | } 152 | 153 | virtual void setBeginningTimeout(milliseconds_t timeOut) { 154 | beginningTimeout = timeOut; 155 | } 156 | 157 | virtual milliseconds_t getBeginningTimeout() const { 158 | return beginningTimeout; 159 | } 160 | 161 | unsigned int getBufferSize() const { 162 | return bufferSize; 163 | } 164 | 165 | /** 166 | * Sets the markExcess, a number (possibly negative) to be subtracted from the on-durations 167 | * and added to the off.durations. 168 | * @param markExcess_ possibly negative new value 169 | */ 170 | void setMarkExcess(int16_t markExcess_) { 171 | markExcess = markExcess_; 172 | } 173 | 174 | /** 175 | * Gets the markExcess, a number (possibly negative) to be subtracted from the on-durations 176 | * and added to the off.durations. 177 | * @return markExcess 178 | */ 179 | int16_t getMarkExcess() const { 180 | return markExcess; 181 | } 182 | }; 183 | -------------------------------------------------------------------------------- /src/boards/Due.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | // This is basically copied from 19 | // https://github.com/enternoescape/Arduino-IRremote-Due 20 | // (License: LGPL 2.1) 21 | // with minor adjustments. 22 | 23 | #pragma once 24 | 25 | #define CURRENT_CLASS Due 26 | 27 | #define HAS_FLASH_READ 1 28 | #define HAS_HARDWARE_PWM 1 29 | #define HAS_SAMPLING 1 30 | #define HAS_INPUT_CAPTURE 0 31 | 32 | #define STRCPY_PF_CAST(x) static_cast(x) 33 | 34 | class Due : public Board { 35 | public: 36 | Due() { 37 | }; 38 | 39 | private: 40 | 41 | #if defined(IR_USE_PWM0) // pin 34 //////////////////////////////////////////// 42 | #define PWM_PIN 34 43 | #define IR_USE_PWM_PORT PIOC 44 | #define IR_USE_PWM_PERIPH PIO_PERIPH_B 45 | #define IR_USE_PWM_PINMASK PIO_PC2 46 | #define IR_USE_PWM_CH 0 47 | #elif defined(IR_USE_PWM1) // pin 36 ////////////////////////////////////////// 48 | #define PWM_PIN 36 49 | #define IR_USE_PWM_PORT PIOC 50 | #define IR_USE_PWM_PERIPH PIO_PERIPH_B 51 | #define IR_USE_PWM_PINMASK PIO_PC4 52 | #define IR_USE_PWM_CH 1 53 | #elif defined(IR_USE_PWM2) // pin 38 ////////////////////////////////////////// 54 | #define PWM_PIN 38 55 | #define IR_USE_PWM_PORT PIOC 56 | #define IR_USE_PWM_PERIPH PIO_PERIPH_B 57 | #define IR_USE_PWM_PINMASK PIO_PC6 58 | #define IR_USE_PWM_CH 2 59 | #elif defined(IR_USE_PWM3) // pin 40 ////////////////////////////////////////// 60 | #define PWM_PIN 40 61 | #define IR_USE_PWM_PORT PIOC 62 | #define IR_USE_PWM_PERIPH PIO_PERIPH_B 63 | #define IR_USE_PWM_PINMASK PIO_PC8 64 | #define IR_USE_PWM_CH 3 65 | #elif defined(IR_USE_PWM4) // pin 9 /////////////////////////////////////////// 66 | #define PWM_PIN 9 67 | #define IR_USE_PWM_PORT PIOC 68 | #define IR_USE_PWM_PERIPH PIO_PERIPH_B 69 | #define IR_USE_PWM_PINMASK PIO_PC21 70 | #define IR_USE_PWM_CH 4 71 | #elif defined(IR_USE_PWM5) // pin 8 /////////////////////////////////////////// 72 | #define PWM_PIN 8 73 | #define IR_USE_PWM_PORT PIOC 74 | #define IR_USE_PWM_PERIPH PIO_PERIPH_B 75 | #define IR_USE_PWM_PINMASK PIO_PC22 76 | #define IR_USE_PWM_CH 5 77 | #elif defined(IR_USE_PWM6) // pin 7 /////////////////////////////////////////// 78 | #define PWM_PIN 7 79 | #define IR_USE_PWM_PORT PIOC 80 | #define IR_USE_PWM_PERIPH PIO_PERIPH_B 81 | #define IR_USE_PWM_PINMASK PIO_PC23 82 | #define IR_USE_PWM_CH 6 83 | #elif defined(IR_USE_PWM7) // pin 6 /////////////////////////////////////////// 84 | #define PWM_PIN 6 85 | #define IR_USE_PWM_PORT PIOC 86 | #define IR_USE_PWM_PERIPH PIO_PERIPH_B 87 | #define IR_USE_PWM_PINMASK PIO_PC24 88 | #define IR_USE_PWM_CH 7 89 | #endif 90 | 91 | #if defined(IR_USE_TC3) // Timer clock 3 ////////////////////////////////////// 92 | #define IR_USE_TC_IRQ TC3_IRQn 93 | #define IR_USE_TC TC1 94 | #define IR_USE_CH 0 95 | #define TIMER_INTR_NAME TC3_Handler 96 | #elif defined(IR_USE_TC4) // Timer clock 4 //////////////////////////////////// 97 | #define IR_USE_TC_IRQ TC4_IRQn 98 | #define IR_USE_TC TC1 99 | #define IR_USE_CH 1 100 | #define TIMER_INTR_NAME TC4_Handler 101 | #elif defined(IR_USE_TC5) // Timer clock 5 //////////////////////////////////// 102 | #define IR_USE_TC_IRQ TC5_IRQn 103 | #define IR_USE_TC TC1 104 | #define IR_USE_CH 2 105 | #define TIMER_INTR_NAME TC5_Handler 106 | #endif 107 | 108 | //Clears the interrupt. 109 | void timerReset() { 110 | IR_USE_TC->TC_CHANNEL[IR_USE_CH].TC_SR; 111 | } 112 | 113 | void timerEnablePwm() { 114 | PWMC_EnableChannel(PWM_INTERFACE, IR_USE_PWM_CH); 115 | }; 116 | 117 | void timerDisablePwm() { 118 | PWMC_DisableChannel(PWM_INTERFACE, IR_USE_PWM_CH); 119 | writeLow(); 120 | }; 121 | 122 | void timerEnableIntr() { 123 | NVIC_EnableIRQ(IR_USE_TC_IRQ); 124 | }; 125 | 126 | void timerDisableIntr() { 127 | NVIC_DisableIRQ(IR_USE_TC_IRQ); 128 | }; 129 | 130 | static const unsigned multiplicator = 20U; // was 2U; 131 | 132 | void timerConfigHz(frequency_t frequency, dutycycle_t dutyCycle) { 133 | pmc_enable_periph_clk(PWM_INTERFACE_ID); 134 | const uint32_t pwmval = frequency * multiplicator; 135 | PWMC_ConfigureClocks(PWM_FREQUENCY * PWM_MAX_DUTY_CYCLE, pwmval, F_CPU); 136 | PIO_Configure(IR_USE_PWM_PORT, IR_USE_PWM_PERIPH, IR_USE_PWM_PINMASK, PIO_DEFAULT); 137 | PWMC_ConfigureChannel(PWM_INTERFACE, IR_USE_PWM_CH, PWM_CMR_CPRE_CLKB, 0U, 0U); 138 | PWMC_SetPeriod(PWM_INTERFACE, IR_USE_PWM_CH, multiplicator); 139 | PWMC_SetDutyCycle(PWM_INTERFACE, IR_USE_PWM_CH, static_cast((multiplicator * dutyCycle + 50U) / 100U)); 140 | }; 141 | 142 | void timerConfigNormal() { 143 | pmc_enable_periph_clk(static_cast(IR_USE_TC_IRQ)); 144 | TC_Configure(IR_USE_TC, IR_USE_CH, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1); 145 | const uint32_t rc = F_CPU / 2U * microsPerTick / 1000000U; 146 | TC_SetRA(IR_USE_TC, IR_USE_CH, rc / 2U); 147 | TC_SetRC(IR_USE_TC, IR_USE_CH, rc); 148 | TC_Start(IR_USE_TC, IR_USE_CH); 149 | IR_USE_TC->TC_CHANNEL[IR_USE_CH].TC_IER = TC_IER_CPCS; 150 | IR_USE_TC->TC_CHANNEL[IR_USE_CH].TC_IDR = ~TC_IER_CPCS; 151 | }; 152 | }; -------------------------------------------------------------------------------- /src/boards/ATmega328P.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | /** 19 | * @file ATmega328P.h 20 | * 21 | * @brief Hardware dependent definitions for boards based on the ATMega 328P. 22 | * This includes the Arduino Uno, Nano, and many others. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "avr.h" 28 | 29 | #define CURRENT_CLASS ATmega328P 30 | 31 | class ATmega328P : public Board { 32 | public: 33 | 34 | ///////////////////////////////////////////////////////////// 35 | #ifdef IR_USE_TIMER1 36 | 37 | void timerEnablePwm() { 38 | TCCR1A |= _BV(COM1A1); 39 | }; 40 | 41 | void timerDisablePwm() { 42 | TCCR1A &= ~(_BV(COM1A1)); 43 | }; 44 | 45 | void timerEnableIntr() { 46 | TIMSK1 = _BV(OCIE1A); 47 | }; 48 | 49 | void timerDisableIntr() { 50 | TIMSK1 = 0U; 51 | }; 52 | 53 | #define TIMER_INTR_NAME TIMER1_COMPA_vect 54 | 55 | void timerConfigHz(frequency_t val, dutycycle_t dutyCycle) { 56 | const uint16_t pwmval = F_CPU / 2U / val; 57 | TCCR1A = _BV(WGM11); 58 | TCCR1B = _BV(WGM13) | _BV(CS10); 59 | ICR1 = pwmval; 60 | OCR1A = pwmval * dutyCycle / 100U; 61 | }; 62 | 63 | void timerConfigNormal() { 64 | TCCR1A = 0U; 65 | TCCR1B = _BV(WGM12) | _BV(CS10); 66 | OCR1A = F_CPU * microsPerTick / 1000000UL; 67 | TCNT1 = 0U; 68 | }; 69 | 70 | #define PWM_PIN 9 71 | ////////////////////////////////////////////////////////////////////////// 72 | #elif defined(IR_USE_TIMER2) || defined(DOXYGEN) // ! defined(IR_USE_TIMER1) 73 | 74 | void timerEnablePwm() { 75 | TCCR2A |= _BV(COM2B1); 76 | } 77 | 78 | void timerDisablePwm() { 79 | TCCR2A &= ~(_BV(COM2B1)); 80 | } 81 | 82 | void timerEnableIntr() { 83 | TIMSK2 = _BV(OCIE2A); 84 | }; 85 | 86 | void timerDisableIntr() { 87 | TIMSK2 = 0U; 88 | }; 89 | 90 | #define TIMER_INTR_NAME TIMER2_COMPA_vect 91 | 92 | void timerConfigHz(frequency_t val, dutycycle_t dutyCycle) { 93 | const uint8_t pwmval = F_CPU / 2U / val; 94 | TCCR2A = _BV(WGM20); 95 | TCCR2B = _BV(WGM22) | _BV(CS20); 96 | OCR2A = pwmval; 97 | OCR2B = pwmval * dutyCycle / 100U; 98 | }; 99 | 100 | void timerConfigNormal() { 101 | TCCR2A = _BV(WGM21); 102 | TCCR2B = _BV(CS21); 103 | OCR2A = F_CPU * microsPerTick / 1000000UL / 8U; 104 | TCNT2 = 0U; 105 | } 106 | ///////////////////////////////////////////////////////////////////////// 107 | 108 | #define PWM_PIN 3 109 | 110 | #else // ! defined(IR_USE_TIMER2) 111 | 112 | #error Config error, either IR_USE_TIMER1 or IR_USE_TIMER2 must be defined. 113 | 114 | #endif 115 | }; 116 | 117 | /* From http://busyducks.com/ascii-art-arduinos 118 | 119 | 120 | 121 | +----[PWR]-------------------| USB |--+ 122 | | +-----+ | 123 | | GND/RST2 [ ][ ] | 124 | | MOSI2/SCK2 [ ][ ] A5/SCL[ ] | C5 125 | | 5V/MISO2 [ ][ ] A4/SDA[ ] | C4 126 | | AREF[ ] | 127 | | GND[ ] | 128 | | [ ]N/C SCK/13[ ] | B5 129 | | [ ]v.ref MISO/12[ ] | . 130 | | [ ]RST MOSI/11[ ]~| . 131 | | [ ]3V3 +---+ 10[ ]~| . 132 | | [ ]5v | A | 9[ ]~| . 133 | | [ ]GND -| R |- 8[ ] | B0 134 | | [ ]GND -| D |- | 135 | | [ ]Vin -| U |- 7[ ] | D7 136 | | -| I |- 6[ ]~| . 137 | | [ ]A0 -| N |- 5[ ]~| . 138 | | [ ]A1 -| O |- 4[ ] | . 139 | | [ ]A2 +---+ INT1/3[ ]~| . 140 | | [ ]A3 INT0/2[ ] | . 141 | | [ ]A4/SDA RST SCK MISO TX>1[ ] | . 142 | | [ ]A5/SCL [ ] [ ] [ ] RX<0[ ] | D0 143 | | [ ] [ ] [ ] | 144 | | UNO_R3 GND MOSI 5V ____________/ 145 | \_______________________/ 146 | 147 | 148 | +-----+ 149 | +------------| USB |------------+ 150 | | +-----+ | 151 | B5 | [ ]D13/SCK MISO/D12[ ] | B4 152 | | [ ]3.3V MOSI/D11[ ]~| B3 153 | | [ ]V.ref ___ SS/D10[ ]~| B2 154 | C0 | [ ]A0 / N \ D9[ ]~| B1 155 | C1 | [ ]A1 / A \ D8[ ] | B0 156 | C2 | [ ]A2 \ N / D7[ ] | D7 157 | C3 | [ ]A3 \_0_/ D6[ ]~| D6 158 | C4 | [ ]A4/SDA D5[ ]~| D5 159 | C5 | [ ]A5/SCL D4[ ] | D4 160 | | [ ]A6 INT1/D3[ ]~| D3 161 | | [ ]A7 INT0/D2[ ] | D2 162 | | [ ]5V GND[ ] | 163 | C6 | [ ]RST RST[ ] | C6 164 | | [ ]GND 5V MOSI GND TX1[ ] | D0 165 | | [ ]Vin [ ] [ ] [ ] RX1[ ] | D1 166 | | [ ] [ ] [ ] | 167 | | MISO SCK RST | 168 | | NANO-V3 | 169 | +-------------------------------+ 170 | 171 | */ 172 | -------------------------------------------------------------------------------- /src/IrWidgetAggregating.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Michael Dreher 2 | // this code may be distributed under the terms of the General Public License V2 (GPL V2) 3 | 4 | // This is a slight reorganization of the original code, by Bengt Martensson. 5 | 6 | #include "IrWidgetAggregating.h" 7 | 8 | #if HAS_INPUT_CAPTURE 9 | 10 | static constexpr frequency_t min_frequency = 20000U; 11 | 12 | IrWidgetAggregating::IrWidgetAggregating(size_t captureLength, 13 | bool pullup, 14 | int16_t markExcess, 15 | milliseconds_t beginningTimeout, 16 | milliseconds_t endingTimeout) 17 | : IrWidget(captureLength, pullup, markExcess, beginningTimeout, endingTimeout) { 18 | } 19 | 20 | IrWidgetAggregating *IrWidgetAggregating::instance = nullptr; 21 | 22 | IrWidgetAggregating *IrWidgetAggregating::newIrWidgetAggregating(size_t captureLength, 23 | bool pullup, 24 | int16_t markExcess, 25 | milliseconds_t beginningTimeout, 26 | milliseconds_t endingTimeout) { 27 | if (instance != nullptr) 28 | return nullptr; 29 | instance = new IrWidgetAggregating(captureLength, pullup, markExcess, 30 | beginningTimeout, endingTimeout); 31 | return instance; 32 | } 33 | 34 | void IrWidgetAggregating::deleteInstance() { 35 | delete instance; 36 | instance = nullptr; 37 | } 38 | 39 | // Wait for a signal on pin ICP1 and store the captured time values in the array 'captureData' 40 | void IrWidgetAggregating::capture() { 41 | #ifdef ARDUINO 42 | uint32_t timeForBeginTimeout = millis() + beginningTimeout; 43 | uint8_t tccr0b = TCCR0B; 44 | //TCCR0B &= ~(_BV(CS02) | _BV(CS01) | _BV(CS00)); // stop timer0 (disables timer IRQs) 45 | 46 | uint16_t period = (F_CPU / min_frequency) >> CAPTURE_PRESCALER_BITS; // the time of one period in CPU clocks 47 | //uint16_t aggThreshold = (period * 10UL) / 8UL; // 65 us = (1/20kHz * 130%) might be a good starting point 48 | uint16_t aggThreshold = period * 2U; 49 | uint8_t icesn_val = _BV(CAT2(ICES, CAP_TIM)); 50 | uint8_t tccrnb = CAT3(TCCR, CAP_TIM, B); 51 | if (invertingSensor) 52 | tccrnb &= ~icesn_val; // trigger on falling edge 53 | else 54 | tccrnb |= icesn_val; // trigger on rising edge 55 | 56 | CAT3(TCCR, CAP_TIM, B) = tccrnb; 57 | OCR1A = CAT2(TCNT, CAP_TIM) - 1; 58 | CAT2(TIFR, CAP_TIM) = _BV(CAT2(ICF, CAP_TIM)) 59 | | _BV(CAT3(OCF, CAP_TIM, CAP_TIM_OC)) | _BV(CAT2(TOV, CAP_TIM)); // clear all timer flags 60 | uint8_t tifr; // cache the result of reading TIFR1 (masked with ICF1 and OCF1A) 61 | uint8_t calShiftM1 = 1; 62 | uint8_t calCount = 1 << (calShiftM1 + 1); 63 | uint8_t aggCount = 0; 64 | ovlBitsDataType ovlCnt = 0; 65 | uint16_t val; 66 | uint16_t prevVal = 0; 67 | uint16_t *pCapDat = captureData; // pointer to current item in captureData[] 68 | uint32_t aggVal = 0; 69 | uint32_t diffVal; 70 | 71 | // disabling IRQs for a long time will disconnect the USB connection of the ATmega32U4, therefore we 72 | // defer the sbi() instruction until we got the starting edge and only stop the Timer0 in the meanwhile 73 | uint8_t sreg = SREG; 74 | debugPinClear(); 75 | 76 | ///////////////////////////////////////// 77 | // wait for first edge 78 | while (!(tifr = (CAT2(TIFR, CAP_TIM) & (_BV(CAT2(ICF, CAP_TIM)))))) { 79 | if (millis() >= timeForBeginTimeout) { 80 | timeouted = true; 81 | goto endCapture; 82 | } 83 | } 84 | TCCR0B &= ~(_BV(CS02) | _BV(CS01) | _BV(CS00)); // stop timer0 (disables timer IRQs) 85 | debugPinToggle(); 86 | val = CAT2(ICR, CAP_TIM); 87 | CAT3(OCR, CAP_TIM, CAP_TIM_OC) = val; // timeout based on previous trigger time 88 | 89 | noInterrupts(); // disable IRQs after the first edge 90 | 91 | // clear input capture and output compare flag bit 92 | CAT2(TIFR, CAP_TIM) = _BV(CAT2(ICF, CAP_TIM)) | _BV(CAT3(OCF, CAP_TIM, CAP_TIM_OC)); 93 | prevVal = val; 94 | 95 | ///////////////////////////////////////// 96 | // wait for all following edges 97 | for (; pCapDat <= &captureData[bufferSize - sampleSize];) // 2 values are stored in each loop, TODO: change to 3 when adding the aggCount 98 | { 99 | debugPinToggle(); 100 | // wait for edge or overflow (output compare match) 101 | while (!(tifr = 102 | (CAT2(TIFR, CAP_TIM) & (_BV(CAT2(ICF, CAP_TIM)) | _BV(CAT3(OCF, CAP_TIM, CAP_TIM_OC)))))) { 103 | } 104 | debugPinToggle(); 105 | val = CAT2(ICR, CAP_TIM); 106 | CAT3(OCR, CAP_TIM, CAP_TIM_OC) = val; // timeout based on previous trigger time 107 | 108 | if (tifr & _BV(CAT3(OCF, CAP_TIM, CAP_TIM_OC))) // check for overflow bit 109 | { 110 | if (ovlCnt >= endingTimeout) // TODO: handle this check together with the check for the pulse length (if packTimeValNormal can handle the value) 111 | { 112 | if (aggVal > 0) { 113 | // TODO check is to value is small enough to be stored 114 | *pCapDat = packTimeVal/*Normal*/(aggVal); // store the pulse length 115 | pCapDat++; 116 | *pCapDat = packTimeVal/*Normal*/((uint32_t) endingTimeout << 16); 117 | pCapDat++; 118 | } 119 | break; // maximum value reached, treat this as timeout and abort capture 120 | } 121 | ovlCnt++; 122 | // clear input capture and output compare flag bit 123 | CAT2(TIFR, CAP_TIM) = _BV(CAT2(ICF, CAP_TIM)) | _BV(CAT3(OCF, CAP_TIM, CAP_TIM_OC)); 124 | continue; 125 | } 126 | 127 | // clear input capture and output compare flag bit 128 | CAT2(TIFR, CAP_TIM) = _BV(CAT2(ICF, CAP_TIM)) | _BV(CAT3(OCF, CAP_TIM, CAP_TIM_OC)); 129 | 130 | diffVal = ((val - prevVal) & 0xffff) | ((uint32_t) ovlCnt << 16); 131 | ovlCnt = 0; 132 | prevVal = val; 133 | 134 | if (diffVal < aggThreshold) { 135 | aggVal += diffVal; 136 | 137 | // calculate the carrier frequency only within the first burst (often a preamble) 138 | if (calCount) { 139 | aggCount++; // only used to calculate the period 140 | // do a calibration on every aggCount which is a power of two because then dividing by calShiftM1 141 | // (shiftcount - 1) can simply be performed by shifting right 142 | if (aggCount == calCount) { 143 | aggThreshold = aggVal >> calShiftM1; 144 | calShiftM1++; 145 | calCount = calCount << 1; // this will automatically terminate calibrating when calCount is 128 because then (128 << 1) & 0xff = 0 146 | } 147 | } 148 | } else { 149 | *pCapDat = packTimeVal/*Normal*/(aggVal); // store the pulse length 150 | pCapDat++; 151 | // TODO check if value is small enough to be stored 152 | *pCapDat = packTimeVal/*Normal*/(diffVal); // store the pause length 153 | pCapDat++; 154 | 155 | aggVal = 0; 156 | calCount = 0; // avoid further period calculation and calibration 157 | } 158 | } 159 | 160 | endCapture: 161 | debugPinClear(); 162 | 163 | TCCR0B = tccr0b; // re-enable Timer0 164 | SREG = sreg; // enable IRQs 165 | 166 | captureCount = pCapDat - captureData; 167 | if (aggThreshold == period * 2U) { 168 | frequency = 0U; 169 | } else { 170 | uint32_t mediumPeriod = timerValueToNanoSeconds(aggThreshold / 2U); 171 | frequency = (frequency_t) (1000000000L / mediumPeriod); 172 | } 173 | #endif // ARDUINO 174 | } 175 | #endif // TCCR0B 176 | -------------------------------------------------------------------------------- /src/IrSignal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "InfraredTypes.h" 4 | #include "IrSequence.h" 5 | #include "version.h" 6 | 7 | /** 8 | * This class models an IR signal with intro-, repeat-, and ending sequences. 9 | * This class is immutable. 10 | */ 11 | class IrSignal { 12 | public: 13 | static constexpr frequency_t defaultFrequency = 38000U; 14 | static constexpr frequency_t invalidFrequency = static_cast(-1); 15 | static constexpr dutycycle_t noDutyCycle = static_cast(-1); 16 | static constexpr dutycycle_t defaultDutyCycle = noDutyCycle; 17 | static constexpr const char *version = VERSION; 18 | 19 | private: 20 | // Maintainer note: the members being const prohibits assignment operators. 21 | // Alternatively, the members can be made non-const and assignments allowed. 22 | const frequency_t frequency = defaultFrequency; 23 | const dutycycle_t dutyCycle = defaultDutyCycle; 24 | const IrSequence intro; 25 | const IrSequence repeat; 26 | const IrSequence ending; 27 | 28 | public: 29 | /** 30 | * Constructs an empty IrSignal. 31 | */ 32 | IrSignal() {}; 33 | IrSignal(const IrSignal& orig) = default; 34 | IrSignal(IrSignal&& orig) = default; 35 | IrSignal& operator=(const IrSignal& rhs) = default; 36 | IrSignal& operator=(IrSignal&& rhs) = default; 37 | 38 | virtual ~IrSignal() = default; 39 | 40 | /** 41 | * Constructor that "moves" the data pointers. 42 | * This is essentially a convenience function that combines IrSequence and IrSignal constructors. 43 | * 44 | * @param intro 45 | * @param lengthIntro 46 | * @param repeat 47 | * @param lengthRepeat 48 | * @param ending 49 | * @param lengthEnding 50 | * @param frequency 51 | * @param dutyCycle 52 | */ 53 | IrSignal(const microseconds_t *intro, size_t lengthIntro, 54 | const microseconds_t *repeat, size_t lengthRepeat, 55 | const microseconds_t *ending, size_t lengthEnding, 56 | frequency_t frequency = defaultFrequency, 57 | dutycycle_t dutyCycle = defaultDutyCycle); 58 | 59 | /** 60 | * Constructor that "moves" the data pointers. No ending sequence. 61 | * This is essentially a convenience function that combines IrSequence and IrSignal constructors. 62 | * 63 | * @param intro 64 | * @param lengthIntro 65 | * @param repeat 66 | * @param lengthRepeat 67 | * @param frequency 68 | * @param dutyCycle 69 | */ 70 | IrSignal(const microseconds_t *intro, size_t lengthIntro, 71 | const microseconds_t *repeat, size_t lengthRepeat, 72 | frequency_t frequency = defaultFrequency, 73 | dutycycle_t dutyCycle = defaultDutyCycle); 74 | 75 | /** 76 | * Copy constructor. 77 | * @param intro 78 | * @param repeat 79 | * @param ending 80 | * @param frequency 81 | * @param dutyCycle 82 | */ 83 | IrSignal(const IrSequence& intro, const IrSequence& repeat, const IrSequence& ending, 84 | frequency_t frequency = defaultFrequency, dutycycle_t dutyCycle = defaultDutyCycle); 85 | 86 | /** 87 | * Move constructor. 88 | * 89 | * @param intro 90 | * @param repeat 91 | * @param ending 92 | * @param frequency 93 | * @param dutyCycle 94 | */ 95 | IrSignal(IrSequence&& intro, IrSequence&& repeat, IrSequence&& ending, 96 | frequency_t frequency = defaultFrequency, dutycycle_t dutyCycle = defaultDutyCycle); 97 | 98 | /** 99 | * Copy constructors for IrSignals without ending sequence. 100 | * 101 | * @param intro 102 | * @param repeat 103 | * @param frequency 104 | * @param dutyCycle 105 | */ 106 | IrSignal(const IrSequence& intro, const IrSequence& repeat, 107 | frequency_t frequency = defaultFrequency, dutycycle_t dutyCycle = defaultDutyCycle); 108 | 109 | /** 110 | * Move constructor for IrSignals without ending sequence. 111 | * @param intro 112 | * @param repeat 113 | * @param frequency 114 | * @param dutyCycle 115 | */ 116 | IrSignal(IrSequence&& intro, IrSequence&& repeat, 117 | frequency_t frequency = defaultFrequency, dutycycle_t dutyCycle = defaultDutyCycle); 118 | 119 | static IrSignal* readFlash(const microseconds_t *intro, size_t lengthIntro, 120 | const microseconds_t *repeat, size_t lengthRepeat, 121 | const microseconds_t *ending, size_t lengthEnding, 122 | frequency_t frequency = defaultFrequency, 123 | dutycycle_t dutyCycle = defaultDutyCycle); 124 | 125 | static IrSignal* readFlash(const microseconds_t *intro, size_t lengthIntro, 126 | const microseconds_t *repeat, size_t lengthRepeat, 127 | frequency_t frequency = defaultFrequency, 128 | dutycycle_t dutyCycle = defaultDutyCycle); 129 | 130 | frequency_t getFrequency() const { 131 | return frequency; 132 | } 133 | 134 | dutycycle_t getDutyCycle() const { 135 | return dutyCycle; 136 | } 137 | 138 | const IrSequence& getEnding() const { 139 | return ending; 140 | } 141 | 142 | const IrSequence& getRepeat() const { 143 | return repeat; 144 | } 145 | 146 | const IrSequence& getIntro() const { 147 | return intro; 148 | } 149 | 150 | /** 151 | * True if and only if at least one of the sequences is true (= non-empty). 152 | * @return 153 | */ 154 | operator bool() const { return intro || repeat || ending; } 155 | 156 | /** 157 | * Print a human readable representation of the IrSignal on the Stream supplied. 158 | * @param stream Stream onto the output is printed. 159 | * @param usingSigns is true, prepend marks with '+' and gaps with '-'. 160 | */ 161 | void dump(Stream& stream, bool usingSigns = false) const; 162 | 163 | /** 164 | * Print a human readable representation of the IrSignal on the Stream supplied, using signs. 165 | * @param stream Stream onto the output is printed. 166 | */ 167 | void dumpWithSigns(Stream& stream) const { 168 | dump(stream, true); 169 | }; 170 | 171 | /** 172 | * If the frequency is sensible, print it to the stream and return true. 173 | * Otherwise do nothing and return false. 174 | * No extra spaces or line feeds are generated. 175 | */ 176 | bool dumpFrequency(Stream& stream) const { 177 | return dumpFrequency(stream, frequency); 178 | }; 179 | 180 | /** 181 | * Static version of dumpFrequency. 182 | * @param stream Stream onto the output is printed. 183 | * @param frequency modulation frequency 184 | */ 185 | static bool dumpFrequency(Stream& stream, frequency_t frequency); 186 | 187 | /** 188 | * If the duty cycle is sensible, print it to the stream and return true. 189 | * Otherwise do nothing and return false. 190 | * No extra spaces or line feeds are generated. 191 | * @param stream Stream onto the output is printed. 192 | */ 193 | bool dumpDutyCycle(Stream& stream) const { 194 | return dumpDutyCycle(stream, dutyCycle); 195 | } 196 | 197 | /** 198 | * Static version of dumpDutyCycle. 199 | * @param stream Stream onto the output is printed. 200 | * @param dutyCycle 201 | */ 202 | static bool dumpDutyCycle(Stream& stream, dutycycle_t dutyCycle); 203 | 204 | /** 205 | * Implementation of the count semantics, i.e., 206 | * how many repetitions should be sent if the signal is sent noSend times. 207 | * @param noSends number of times to "send signal". 208 | */ 209 | unsigned int noRepetitions(unsigned int noSends) const { 210 | return noSends == 0 ? 0 211 | : intro ? noSends -1 : noSends; 212 | } 213 | }; 214 | -------------------------------------------------------------------------------- /src/Pronto.cpp: -------------------------------------------------------------------------------- 1 | #include "Pronto.h" 2 | #include "IrSignal.h" 3 | #include "Board.h" // for HAS_FLASH_READ 4 | #include 5 | 6 | inline uint16_t divRound(microseconds_t duration, microseconds_t timebase) { 7 | return static_cast((static_cast(duration) + timebase / 2) / timebase); 8 | } 9 | 10 | IrSignal *Pronto::parse(const uint16_t *data, size_t size) { 11 | microseconds_t timebase = (microsecondsInSeconds * data[1] + referenceFrequency/2) / referenceFrequency; 12 | frequency_t frequency; 13 | switch (data[0]) { 14 | case learnedToken: // normal, "learned" 15 | frequency = toFrequency(data[1]); 16 | break; 17 | case learnedNonModulatedToken: // non-demodulated, "learned" 18 | frequency = 0U; 19 | break; 20 | default: 21 | return nullptr; 22 | } 23 | size_t introPairs = data[2]; 24 | size_t repetitionPairs = data[3]; 25 | if (numbersInPreamble + 2*(introPairs + repetitionPairs) != size) // inconsistent sizes 26 | return nullptr; 27 | 28 | microseconds_t* intro = mkSequence(data + numbersInPreamble, introPairs, timebase); 29 | microseconds_t* repeat = mkSequence(data + numbersInPreamble + 2*introPairs, repetitionPairs, timebase); 30 | 31 | return new IrSignal(intro, 2*introPairs, repeat, 2*repetitionPairs, frequency); 32 | } 33 | 34 | IrSignal *Pronto::parse(const char *str) { 35 | size_t len = strlen(str)/(digitsInProntoNumber + 1) + 1; 36 | uint16_t* data = new uint16_t[len]; 37 | const char *p = str; 38 | char *endptr[1]; 39 | for (unsigned int i = 0; i < len; i++) { 40 | long x = strtol(p, endptr, 16); 41 | if (x == 0 && i >= numbersInPreamble) { 42 | // Alignment error?, bail immediately (often right result). 43 | len = i; 44 | break; 45 | } 46 | data[i] = x; // If input is conforming, there can be no overflow! 47 | p = *endptr; 48 | } 49 | IrSignal *res = parse(data, len); 50 | delete [] data; 51 | return res; 52 | } 53 | 54 | #if HAS_FLASH_READ || defined(DOXYGEN) 55 | IrSignal *Pronto::parse_PF(uint_farptr_t str) { 56 | size_t len = strlen_PF(STRCPY_PF_CAST(str)); 57 | char work[len + 1]; 58 | strncpy_PF(work, STRCPY_PF_CAST(str), len); 59 | return parse(work); 60 | } 61 | 62 | IrSignal *Pronto::parse_PF(const char *str) { 63 | return parse_PF(reinterpret_cast(str)); // to avoid infinite recursion 64 | }; 65 | 66 | IrSignal *Pronto::parse(const __FlashStringHelper *str) { 67 | return parse_PF(reinterpret_cast(str)); 68 | } 69 | #endif 70 | 71 | microseconds_t* Pronto::mkSequence(const uint16_t* data, size_t noPairs, microseconds_t timebase) { 72 | microseconds_t *durations = new microseconds_t[2*noPairs]; 73 | for (unsigned int i = 0; i < 2*noPairs; i++) { 74 | uint32_t duration = static_cast(data[i]) * timebase; 75 | durations[i] = (duration <= MICROSECONDS_T_MAX) ? duration : MICROSECONDS_T_MAX; 76 | } 77 | return durations; 78 | } 79 | 80 | frequency_t Pronto::toFrequency(uint16_t code) { 81 | return referenceFrequency / code; 82 | } 83 | 84 | uint16_t Pronto::toFrequencyCode(frequency_t frequency) { 85 | return referenceFrequency / effectiveFrequency(frequency); 86 | } 87 | 88 | frequency_t Pronto::effectiveFrequency(frequency_t frequency) { 89 | return frequency > 0 ? frequency : fallbackFrequency; 90 | } 91 | 92 | microseconds_t Pronto::toTimebase(frequency_t frequency) { 93 | return microsecondsInSeconds / effectiveFrequency(frequency); 94 | } 95 | 96 | size_t Pronto::lengthHexString(size_t introLength, size_t repeatLength) { 97 | return (digitsInProntoNumber + 1) * (numbersInPreamble + introLength + repeatLength); 98 | } 99 | 100 | char Pronto::hexDigit(unsigned int x) { 101 | return x <= 9 ? ('0' + x) : ('A' + (x - 10)); 102 | } 103 | 104 | void Pronto::appendChar(char *result, unsigned int& index, char ch) { 105 | result[index] = ch; 106 | index++; 107 | } 108 | 109 | void Pronto::appendDigit(char *result, unsigned int& index, unsigned int number) { 110 | appendChar(result, index, hexDigit(number)); 111 | } 112 | 113 | void Pronto::appendNumber(char *result, unsigned int& index, uint16_t number) { 114 | for (unsigned int i = 0; i < digitsInProntoNumber; i++) { 115 | unsigned int shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i); 116 | appendDigit(result, index, (number >> shifts) & hexMask); 117 | } 118 | appendChar(result, index, ' '); 119 | } 120 | 121 | void Pronto::appendDuration(char *result, unsigned int& index, microseconds_t duration, microseconds_t timebase) { 122 | appendNumber(result, index, divRound(duration, timebase)); 123 | } 124 | 125 | void Pronto::appendSequence(char *result, unsigned int& index, const microseconds_t *data, size_t length, microseconds_t timebase) { 126 | for (unsigned int i = 0; i < length; i++) 127 | appendDuration(result, index, data[i], timebase); 128 | } 129 | 130 | void Pronto::appendSequence(char *result, unsigned int& index, const IrSequence& irSequence, microseconds_t timebase) { 131 | appendSequence(result, index, irSequence.getDurations(), irSequence.getLength(), timebase); 132 | } 133 | 134 | char* Pronto::prelude(frequency_t frequency, size_t introLength, size_t repeatLength) { 135 | char *result = new char[lengthHexString(introLength, repeatLength)]; 136 | unsigned int index = 0; 137 | appendNumber(result, index, frequency > 0 ? learnedToken : learnedNonModulatedToken); 138 | appendNumber(result, index, toFrequencyCode(frequency)); 139 | appendNumber(result, index, introLength / 2); 140 | appendNumber(result, index, repeatLength / 2); 141 | return result; 142 | } 143 | 144 | char* Pronto::toProntoHex(const microseconds_t* introData, size_t introLength, const microseconds_t* repeatData, size_t repeatLength, frequency_t frequency) { 145 | char *result = prelude(frequency, introLength, repeatLength); 146 | unsigned int index = numbersInPreamble * (digitsInProntoNumber + 1); 147 | microseconds_t timebase = toTimebase(frequency); 148 | appendSequence(result, index, introData, introLength, timebase); 149 | appendSequence(result, index, repeatData, repeatLength, timebase); 150 | result[index - 1] = '\0'; 151 | return result; 152 | } 153 | 154 | void Pronto::dump(Stream& stream, const microseconds_t* introData, size_t introLength, const microseconds_t* repeatData, size_t repeatLength, frequency_t frequency) { 155 | dumpNumber(stream, frequency > 0 ? learnedToken : learnedNonModulatedToken); 156 | stream.print(' '); 157 | dumpNumber(stream, toFrequencyCode(frequency)); 158 | stream.print(' '); 159 | dumpNumber(stream, introLength / 2); 160 | stream.print(' '); 161 | dumpNumber(stream, repeatLength / 2); 162 | stream.print(' '); 163 | microseconds_t timebase = toTimebase(frequency); 164 | dumpSequence(stream, introData, introLength, timebase); 165 | stream.print(' '); 166 | dumpSequence(stream, repeatData, repeatLength, timebase); 167 | } 168 | 169 | void Pronto::dumpSequence(Stream& stream, const microseconds_t *data, size_t length, microseconds_t timebase) { 170 | for (unsigned int i = 0; i < length; i++) { 171 | dumpDuration(stream, data[i], timebase); 172 | if (i < length - 1) 173 | stream.print(' '); 174 | } 175 | } 176 | 177 | void Pronto::dumpDuration(Stream& stream, microseconds_t duration, microseconds_t timebase) { 178 | dumpNumber(stream, divRound(duration, timebase)); 179 | } 180 | 181 | void Pronto::dumpNumber(Stream& stream, uint16_t number) { 182 | for (unsigned int i = 0; i < digitsInProntoNumber; i++) { 183 | unsigned int shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i); 184 | dumpDigit(stream, (number >> shifts) & hexMask); 185 | } 186 | } 187 | 188 | void Pronto::dumpDigit(Stream& stream, unsigned int number) { 189 | stream.print(hexDigit(number)); 190 | } 191 | -------------------------------------------------------------------------------- /src/Pronto.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Static class consisting of functions for parsing a Pronto Hex string (like 0000 006C 0022 0002 015B 00AD ...) into an IrSignal, 3 | * and vice versa. 4 | * Reference. 5 | * 6 | * Note: Unless you have "infinitely much" memory, it is a very bad idea to put Pronto Hex in your source files. 7 | * Better is to use, for example IrScruitinizer to convert the signals offline, 8 | * and put the converted version in your source files instead. 9 | */ 10 | 11 | // Maintainer note: 12 | // Make sure that this class does not use floating point arithmetics. 13 | 14 | #pragma once 15 | 16 | #include "InfraredTypes.h" 17 | #include "IrSignal.h" 18 | #include "Board.h" 19 | 20 | class Pronto { 21 | public: 22 | static constexpr unsigned int digitsInProntoNumber = 4U; 23 | static constexpr unsigned int numbersInPreamble = 4U; 24 | 25 | private: 26 | static constexpr unsigned int bitsInHexadecimal = 4U; 27 | static constexpr unsigned int hexMask = 0xFU; 28 | static constexpr uint16_t learnedToken = 0x0000U; 29 | static constexpr uint16_t learnedNonModulatedToken = 0x0100U; 30 | static constexpr uint32_t referenceFrequency = 4145146UL; 31 | static constexpr uint16_t fallbackFrequencyCode = 0x0040U; // To use with frequency = 0; 32 | static constexpr frequency_t fallbackFrequency = 64767U; // To use with frequency = 0; 33 | static constexpr uint32_t microsecondsInSeconds = 1000000UL; 34 | 35 | Pronto() = delete; 36 | 37 | static microseconds_t* mkSequence(const uint16_t *data, size_t pairs, microseconds_t timebase); 38 | 39 | static frequency_t toFrequency(uint16_t code); 40 | 41 | static uint16_t toFrequencyCode(frequency_t frequency); 42 | 43 | static frequency_t effectiveFrequency(frequency_t frequency); 44 | 45 | static microseconds_t toTimebase(frequency_t frequency_t); 46 | 47 | static size_t lengthHexString(size_t introLength, size_t repeatLength); 48 | 49 | static char* prelude(frequency_t frequency, size_t introLength, size_t repeatLength); 50 | 51 | static char hexDigit(unsigned int x); 52 | 53 | static void appendChar(char *result, unsigned int& index, char ch); 54 | 55 | static void appendDuration(char *result, unsigned int& index, uint16_t duration, microseconds_t timebase); 56 | 57 | static void appendDigit(char *result, unsigned int& index, unsigned int number); 58 | 59 | static void appendNumber(char *result, unsigned int& index, uint16_t number); 60 | 61 | static void appendSequence(char *result, unsigned int& index, const microseconds_t *data, size_t length, microseconds_t timebase); 62 | 63 | static void appendSequence(char *result, unsigned int& index, const IrSequence& irSequence, microseconds_t timebase); 64 | 65 | static void dumpSequence(Stream& stream, const microseconds_t *data, size_t length, microseconds_t timebase); 66 | 67 | static void dumpDuration(Stream& stream, microseconds_t duration, microseconds_t timebase); 68 | 69 | static void dumpNumber(Stream& stream, uint16_t number); 70 | 71 | static void dumpDigit(Stream& stream, unsigned int number); 72 | 73 | public: 74 | /** 75 | * Function for parsing its input data into an IrSignal. The ending sequence will always be empty. 76 | * @param data Numerical data, the number in the Pronto form. 77 | * @param size Number of data points. 78 | * @return IrSignal 79 | */ 80 | static IrSignal *parse(const uint16_t *data, size_t size); 81 | 82 | /** 83 | * Function for parsing its input data into an IrSignal. The ending sequence will always be empty. 84 | * @param str Text string containing a Pronto form signal. 85 | * @return IrSignal 86 | */ 87 | static IrSignal *parse(const char *str); 88 | 89 | #if HAS_FLASH_READ || defined(DOXYGEN) 90 | /** 91 | * Function for parsing its input data into an IrSignal. The ending sequence will always be empty. 92 | * @param str Text string containing a Pronto form signal. 93 | * @return IrSignal 94 | */ 95 | static IrSignal *parse_PF(const uint_farptr_t str); 96 | 97 | static IrSignal *parse_PF(const char * ptr); 98 | 99 | /** 100 | * Function for parsing its input data into an IrSignal. The ending sequence will always be empty. 101 | * @param str Text string containing a Pronto form signal. 102 | * This form handles the F(...) form. 103 | * Available only on platforms implementing the str*_PF functions. 104 | * @return IrSignal 105 | */ 106 | static IrSignal *parse(const __FlashStringHelper *str); 107 | #endif 108 | 109 | /** 110 | * Function for generating a Pronto Hex string from the argument. 111 | * @param irSignal 112 | * @return Zero terminated string. Has been generated by new[], and must be manually delete[]-d by the user. 113 | */ 114 | static char* toProntoHex(const IrSignal& irSignal) { 115 | return toProntoHex(irSignal.getIntro(), irSignal.getRepeat(), irSignal.getFrequency()); 116 | } 117 | 118 | /** 119 | * Function for generating a Pronto Hex string from the arguments. 120 | * 121 | * @param introSequence 122 | * @param repeatSequence 123 | * @param frequency 124 | * @return Zero terminated string. Has been generated by new[], and must be manually delete[]-d by the user. 125 | */ 126 | static char* toProntoHex(const IrSequence& introSequence, const IrSequence& repeatSequence = IrSequence::emptyInstance, frequency_t frequency = IrSignal::defaultFrequency) { 127 | return toProntoHex(introSequence.getDurations(), introSequence.getLength(), repeatSequence.getDurations(), repeatSequence.getLength(), frequency); 128 | } 129 | 130 | /** 131 | * Function for generating a Pronto Hex string from the arguments. 132 | * 133 | * @param introData 134 | * @param introLength 135 | * @param frequency 136 | * @return Zero terminated string. Has been generated by new[], and must be manually delete[]-d by the user. 137 | */ 138 | static char* toProntoHex(const microseconds_t* introData, size_t introLength, frequency_t frequency = IrSignal::defaultFrequency) { 139 | return toProntoHex(introData, introLength, nullptr, 0, frequency); 140 | } 141 | 142 | /** 143 | * Function for generating a Pronto Hex string from the arguments. 144 | * 145 | * @param introData 146 | * @param introLength 147 | * @param repeatData 148 | * @param repeatLength 149 | * @param frequency 150 | * @return Zero terminated string. Has been generated by new[], and must be manually delete[]-d by the user. 151 | */ 152 | static char* toProntoHex(const microseconds_t* introData, size_t introLength, const microseconds_t* repeatData = nullptr, size_t repeatLength = 0, frequency_t frequency = IrSignal::defaultFrequency); 153 | 154 | /** 155 | * Function for printing data as Pronto Hex string on the stream given as argument. 156 | * @param stream Stream on which to write 157 | * @param irSignal 158 | */ 159 | static void dump(Stream& stream, const IrSignal& irSignal) { 160 | return dump(stream, irSignal.getIntro(), irSignal.getRepeat(), irSignal.getFrequency()); 161 | } 162 | 163 | /** 164 | * Function for printing data as Pronto Hex string on the stream given as argument. 165 | * 166 | * @param stream Stream on which to write 167 | * @param introSequence 168 | * @param repeatSequence 169 | * @param frequency 170 | */ 171 | static void dump(Stream& stream, const IrSequence& introSequence, const IrSequence& repeatSequence = IrSequence::emptyInstance, frequency_t frequency = IrSignal::defaultFrequency) { 172 | return dump(stream, introSequence.getDurations(), introSequence.getLength(), repeatSequence.getDurations(), repeatSequence.getLength(), frequency); 173 | } 174 | 175 | /** 176 | * Function for printing data as Pronto Hex string on the stream given as argument. 177 | * 178 | * @param stream 179 | * @param introData 180 | * @param introLength 181 | * @param repeatData 182 | * @param repeatLength 183 | * @param frequency 184 | */ 185 | static void dump(Stream& stream, const microseconds_t* introData, size_t introLength, const microseconds_t* repeatData = nullptr, size_t repeatLength = 0, frequency_t frequency = IrSignal::defaultFrequency); 186 | }; -------------------------------------------------------------------------------- /src/IrWidget.h: -------------------------------------------------------------------------------- 1 | /* IR Widget: capture a raw IR signal and dump the timing of the non-demodulated signal 2 | 3 | http://www.piclist.com/images/boards/irwidget/index.htm 4 | http://www.hifi-remote.com/forums/dload.php?action=file&file_id=2044 5 | http://www.hifi-remote.com/wiki/index.php?title=IR_Scope_and_IR_Widget_User%27s_Guide 6 | http://www.compendiumarcana.com/irwidget/ 7 | 8 | Arduino digital pin numbers for the input capture pin (ICP) and the logic analyzer debugging pin (LA Dbg): 9 | Board name / MCU | ICP pin | LA Dbg pin 10 | -------------------------------------------|--------------.-----------|------------------------ 11 | Duemilanove/Uno (ATmega328P / ATmega168) | ICP1/PB0, Arduino pin 8 | PD6, Arduino pin 6 12 | Leonardo (ATmega32U4) | ICP1/PD4, Arduino pin 4 | PD6, Arduino pin 12 13 | Arduino Mega 2560 (ATmega2560) | ICP4/PL0, Arduino pin 49 | PL6, Arduino pin 43 14 | 15 | see also here: 16 | http://arduino.cc/en/Hacking/PinMapping168 (also for ATmega328P) 17 | http://arduino.cc/en/Hacking/PinMapping32u4 18 | http://arduino.cc/en/Hacking/PinMapping2560 19 | */ 20 | 21 | // Copyright (c) 2012 Michael Dreher 22 | // this code may be distributed under the terms of the General Public License V2 (GPL V2) 23 | 24 | // Code slighty reorganized by Bengt Martensson 25 | 26 | //#define ALTERNATE_PIN 27 | #pragma once 28 | 29 | #include 30 | #include "IrReader.h" 31 | #include "Board.h" 32 | 33 | #define ENABLE_PULL_UP 34 | //#define DEBUG_PORT D 35 | //#define DEBUG_PIN 6 36 | //#define DEBUG_PORT L 37 | 38 | #define USE_PRESCALER_FACTOR_8 1 39 | 40 | /** 41 | * Base class for classes based upon ICP pins capture. 42 | * See this article (in German). 43 | */ 44 | class IrWidget : public IrReader { 45 | public: 46 | static constexpr int16_t defaultMarkExcess = 0; 47 | 48 | protected: 49 | frequency_t frequency; 50 | 51 | IrWidget(size_t captureLength = defaultCaptureLength, 52 | bool pullup = false, 53 | int16_t markExcess = defaultMarkExcess, 54 | milliseconds_t beginningTimeout = defaultBeginningTimeout, 55 | milliseconds_t endingTimeout = defaultEndingTimeout); 56 | virtual ~IrWidget(); 57 | 58 | public: 59 | /** 60 | * Set true means if sensor signal is inverted (low = signal on) 61 | * (false has not been tested, and is not supported). 62 | */ 63 | static const bool invertingSensor = true; 64 | 65 | virtual void capture() = 0; 66 | 67 | /** 68 | * For compatibility with the receiver classes, receive is a synonym for capture. 69 | */ 70 | void receive() { 71 | capture(); 72 | } 73 | 74 | size_t getDataLength() const { // was: getCaptureCount() 75 | return captureCount; 76 | } 77 | 78 | bool isReady() const { 79 | return timeouted || !isEmpty(); 80 | } 81 | 82 | void reset() { 83 | captureCount = 0; 84 | } 85 | 86 | microseconds_t inline getDuration(unsigned int i) const { 87 | uint32_t result32 = timerValueToNanoSeconds(unpackTimeVal(captureData[i])) / 1000 88 | + (i & 1 ? markExcess : -markExcess); 89 | return result32 <= MICROSECONDS_T_MAX ? (microseconds_t) result32 : MICROSECONDS_T_MAX; 90 | } 91 | 92 | /** 93 | * Sets the ending timeout. In this implementation, this is effectively 94 | * rounded to the nearest multiple of 32 milliseconds. 95 | * @param timeout timeout in milliseconds. 96 | */ 97 | void setEndingTimeout(milliseconds_t timeout); 98 | 99 | milliseconds_t getEndingTimeout() const; 100 | 101 | frequency_t getFrequency() const { 102 | return frequency; 103 | } 104 | 105 | void dump(Stream &stream) const; 106 | 107 | private: 108 | void setup(bool setup); 109 | 110 | //////////////////////////////////////////////////////////////////////////////// 111 | // Internal defines, don't change 112 | //////////////////////////////////////////////////////////////////////////////// 113 | 114 | protected: 115 | #if USE_PRESCALER_FACTOR_8 116 | #define CAPTURE_PRESCALER_SETTING (_BV(CAT3(CS, CAP_TIM, 1))) 117 | #define CAPTURE_PRESCALER_BITS (3) 118 | #else 119 | #define CAPTURE_PRESCALER_SETTING (_BV(CAT3(CS, CAP_TIM, 0))) 120 | #define CAPTURE_PRESCALER_BITS (0) 121 | #endif 122 | #define CAPTURE_PRESCALER_FACTOR (_BV(CAPTURE_PRESCALER_BITS)) 123 | 124 | #if RANGE_EXTENSION_BITS > 8 125 | typedef uint16_t ovlBitsDataType; 126 | #else 127 | typedef uint8_t ovlBitsDataType; 128 | #endif 129 | 130 | static constexpr uint8_t RANGE_EXTENSION_BITS = 4; // factor for upper measurement range = 2^(RANGE_EXTENSION_BITS+1) 131 | 132 | ovlBitsDataType endingTimeout; // = _BV(RANGE_EXTENSION_BITS) - 1; 133 | 134 | private: 135 | //////////////////////////////////////////////////////////////////////////////// 136 | // Adaption to different MCUs and clk values 137 | //////////////////////////////////////////////////////////////////////////////// 138 | #if defined(_AVR_IOM32U4_H_) 139 | #ifndef ALTERNATE_PIN 140 | // Digital pin 4, ICP1 141 | #define CAP_PORT D 142 | #define CAP_PIN 4 143 | #define CAP_TIM 1 144 | #define CAP_TIM_OC A 145 | #else // Alternate pin 146 | // Digital pin 13, ICP3 147 | #define CAP_PORT C 148 | #define CAP_PIN 7 149 | #define CAP_TIM 3 150 | #define CAP_TIM_OC A 151 | #endif // ALTERNATE_PIN 152 | #elif defined(_AVR_IOM2560_H_) 153 | #ifndef ALTERNATE_PIN 154 | // Digital pin 49, ICP4 155 | #define CAP_PORT L 156 | #define CAP_PIN 0 157 | #define CAP_TIM 4 158 | #define CAP_TIM_OC A 159 | #else // ALTERNATE_PIN 160 | // Digital pin 48, ICP5 161 | #define CAP_PORT L 162 | #define CAP_PIN 1 163 | #define CAP_TIM 5 164 | #define CAP_TIM_OC A 165 | #endif // ALTERNATE_PIN 166 | #else 167 | // the default is the setting for the ATmega328P / ATmega168 168 | // Digital pin 8, ICP1 169 | #define CAP_PORT B 170 | #define CAP_PIN 0 171 | #define CAP_TIM 1 172 | #define CAP_TIM_OC A 173 | #endif 174 | 175 | //////////////////////////////////////////////////////////////////////////////// 176 | // Helper macros 177 | //////////////////////////////////////////////////////////////////////////////// 178 | #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // clear bit 179 | #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // set bit 180 | #define __CAT2(base, portname) base##portname // internally needed by CAT2 181 | #define CAT2(prefix, num) __CAT2(prefix, num) // build a define name from 2 params 182 | #define __CAT3(prefix, num, postfix) prefix##num##postfix // internally needed by CAT3 183 | #define CAT3(prefix, num, postfix) __CAT3(prefix, num, postfix) // build a define name from 3 params 184 | 185 | // these macros are used to debug the timing with an logic analyzer or oscilloscope on a port pin 186 | protected: 187 | inline void debugPinToggle(void) { 188 | #if defined(DEBUG_PIN) && defined(DEBUG_PORT) 189 | CAT2(PIN, DEBUG_PORT) = _BV(DEBUG_PIN); 190 | #endif 191 | } 192 | 193 | inline void debugPinClear(void) { 194 | #if defined(DEBUG_PIN) && defined(DEBUG_PORT) 195 | cbi(CAT2(PORT, DEBUG_PORT), DEBUG_PIN); 196 | #endif 197 | } 198 | uint16_t *captureData; //[bufSize]; // the buffer where the catured data is stored 199 | uint16_t captureCount; // number of values stored in captureData 200 | static constexpr uint8_t sampleSize = 2; 201 | 202 | virtual uint32_t unpackTimeVal(uint32_t val) const = 0; 203 | 204 | // convert number of clocks to nanoseconds, try to use integer arithmetic and avoid 205 | // overflow and too much truncation (double arithmetic costs additional 800 byte of code) 206 | static uint32_t inline timerValueToNanoSeconds(uint32_t x) { 207 | #if (F_CPU % 8000000) == 0 208 | return (x * (125UL << CAPTURE_PRESCALER_BITS)) / (F_CPU / 8000000UL); 209 | #elif (F_CPU % 1000000) == 0 210 | return (x * (1000UL << CAPTURE_PRESCALER_BITS)) / (F_CPU / 1000000UL); 211 | #elif (F_CPU % 115200) == 0 // serial bps rate compatible cpu clocks, e.g. 7372800 or 14745600 212 | // TODO: this has to be tested, especially the accuracy 213 | return (((x * (1000UL << CAPTURE_PRESCALER_BITS)) / (F_CPU / 115200UL)) * 625UL) / 72UL; 214 | #else 215 | // TODO: this has to be tested 216 | return (uint32_t) (((double) x * (1.0E9 * (double) CAPTURE_PRESCALER_FACTOR)) / (double) F_CPU); // use double precision floating point arithmetic 217 | #endif 218 | } 219 | }; 220 | -------------------------------------------------------------------------------- /src/Board.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2020 Bengt Martensson. 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; either version 3 of the License, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program. If not, see http://www.gnu.org/licenses/. 16 | */ 17 | 18 | /** 19 | * This class serves as an HAL (Hardware Abstraction Layer). 20 | * All access to the hardware should go through this class. 21 | * (In particular, using \c digital[Read,Write] and \c \::pinMode is prohibited 22 | * (exception: code that runs exclusively on the host). 23 | * 24 | * It is a singleton class (since there is only one board), instantiated 25 | * automatically to one of its subclasses. 26 | */ 27 | 28 | #pragma once 29 | 30 | #include "IrSignal.h" 31 | #include "PinModeStatus.h" 32 | 33 | class Board { 34 | protected: 35 | Board() { 36 | setupDebugPin(); 37 | }; 38 | 39 | public: 40 | static void delayMicroseconds(microseconds_t); 41 | 42 | virtual void writeLow(); 43 | virtual void writeHigh(); 44 | 45 | virtual void writeLow(pin_t pin) { digitalWrite(pin, LOW); }; 46 | virtual void writeHigh(pin_t pin) { digitalWrite(pin, HIGH); }; 47 | 48 | void setPinMode(pin_t pin, PinMode mode) { pinMode(pin, mode); }; 49 | bool readDigital(pin_t pin) { return digitalRead(pin); }; 50 | 51 | virtual pin_t getPwmPin() const; 52 | 53 | static Board* getInstance() { 54 | return instance; 55 | }; 56 | 57 | static const unsigned long microsPerTick = 50UL; // was USECPERTICK 58 | 59 | virtual void checkValidSendPin(pin_t pin __attribute__((unused))) {/* TODO */}; 60 | 61 | /** 62 | * Constant indicating no or invalid pin. 63 | */ 64 | static constexpr pin_t NO_PIN = 255U; 65 | 66 | /** 67 | * Default duty cycle to use. Do not confuse with IrSignal::defaultDutyCycle. 68 | */ 69 | static constexpr dutycycle_t defaultDutyCycle = 40; 70 | 71 | /** 72 | * Start the periodic ISR sampler routine. Called from IrReceiveSampler. 73 | */ 74 | virtual void enableSampler(pin_t pin __attribute__((unused))) { 75 | timerConfigNormal(); 76 | timerEnableIntr(); 77 | timerReset(); 78 | } 79 | 80 | /** 81 | * Turn off sampler routine. 82 | */ 83 | virtual void disableSampler() { 84 | timerDisableIntr(); 85 | } 86 | 87 | /** 88 | * Start PWM, making output active. 89 | * @param pin 90 | * @param frequency 91 | * @param dutyCycle 92 | */ 93 | void enablePwm(pin_t pin, frequency_t frequency, dutycycle_t dutyCycle) { 94 | checkValidSendPin(pin); 95 | //pinMode(getPin(), OUTPUT); 96 | //writeLow(); 97 | timerConfigHz(frequency, dutyCycle); 98 | } 99 | 100 | /** 101 | * Turn off PWM. 102 | */ 103 | void disablePwm() { 104 | } 105 | 106 | void sendPwmMark(microseconds_t time) { 107 | timerEnablePwm(); // supposed to turn on 108 | delayMicroseconds(time); 109 | timerDisablePwm(); 110 | } 111 | 112 | /** 113 | * TODO 114 | */ 115 | virtual void timerReset() {}; 116 | 117 | protected: 118 | /** 119 | * Start periodic sampling routine. 120 | */ 121 | virtual void timerEnableIntr() = 0; 122 | 123 | /** 124 | * Turn off periodic interrupts. 125 | */ 126 | virtual void timerDisableIntr() = 0; 127 | 128 | /** 129 | * Configure hardware PWM, but do not enable it. 130 | */ 131 | virtual void timerConfigHz(frequency_t hz, dutycycle_t dutyCycle = defaultDutyCycle) = 0; 132 | 133 | /** 134 | * Disables the PWM configuration. 135 | */ 136 | virtual void timerConfigNormal() = 0; 137 | 138 | /** 139 | * Start PWM output. 140 | */ 141 | virtual void timerEnablePwm() = 0; 142 | 143 | /** 144 | * Turn off PWM output. 145 | */ 146 | virtual void timerDisablePwm() = 0; 147 | 148 | public: 149 | // Function defined later in this file 150 | static constexpr pin_t defaultPwmPin(); 151 | 152 | static constexpr bool hasHardwarePwm(); 153 | 154 | static void debugPinHigh(); 155 | 156 | static void debugPinLow(); 157 | 158 | static void setupDebugPin(); 159 | 160 | private: 161 | static Board* instance; 162 | }; 163 | 164 | /////////////////////////////////////////////////////////////////////////////// 165 | //------------------------------------------------------------------------------ 166 | // CPU Frequency 167 | // 168 | #ifndef F_CPU 169 | #error F_CPU not defined. This must be fixed. 170 | #endif 171 | 172 | #if ! defined(ARDUINO) 173 | 174 | // Assume that we compile a test version, to be executed on the host, not on a board. 175 | #include "boards/NoBoard.h" 176 | 177 | #elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) 178 | /////////////////// Arduino Uno, Nano etc (previously default clause) 179 | //#define IR_USE_TIMER1 // tx = pin 9 180 | #define IR_USE_TIMER2 // tx = pin 3 181 | 182 | #include "boards/ATmega328P.h" 183 | 184 | #elif defined(__AVR_ATmega2560__) 185 | /////////////////// Mega 2560 etc ////////////////////////////////// 186 | //#define IR_USE_TIMER1 // tx = pin 11 187 | #define IR_USE_TIMER2 // tx = pin 9 188 | //#define IR_USE_TIMER3 // tx = pin 5 189 | //#define IR_USE_TIMER4 // tx = pin 6 190 | //#define IR_USE_TIMER5 // tx = pin 46 191 | 192 | #include "boards/ATmega2560.h" 193 | 194 | #elif defined(__AVR_ATmega32U4__) 195 | // Can be either Leonardo, Micro, or Teensy 2. 196 | #if defined(ARDUINO_AVR_LEONARDO) || defined(ARDUINO_AVR_MICRO) 197 | #define IR_USE_TIMER1 // tx = 9 198 | //#define IR_USE_TIMER2 // tx = 3, currently broken 199 | 200 | #include "boards/ATmega32U4.h" 201 | 202 | #else 203 | //// Teensy 2.0 ? 204 | #error Unsupported board. Please fix boarddefs.h. 205 | #endif 206 | 207 | #elif defined(__AVR_ATmega4809__) 208 | // ATMega4809, like Uno WiFi Rev 2, Nano Every 209 | #define IR_USE_TIMER1 // tx = pin 6 210 | //#define IR_USE_TIMER2 // Not yet implemented tx = ? 211 | 212 | #include "boards/ATmega4809.h" 213 | 214 | #elif defined(__SAM3X8E__) || defined(__SAM3X8H__) 215 | // Arduino Due 216 | 217 | //#define IR_USE_PWM0 // tx = pin 34 218 | //#define IR_USE_PWM1 // tx = pin 36 219 | //#define IR_USE_PWM2 // tx = pin 38 220 | //#define IR_USE_PWM3 // tx = pin 40 221 | //#define IR_USE_PWM4 // tx = pin 9 222 | //#define IR_USE_PWM5 // tx = pin 8 223 | #define IR_USE_PWM6 // tx = pin 7 224 | //#define IR_USE_PWM7 // tx = pin 6 225 | 226 | #define IR_USE_TC3 // Use timer clock 3. 227 | //#define IR_USE_TC4 // Use timer clock 4. 228 | //#define IR_USE_TC5 // Use timer clock 5. 229 | 230 | // #define IR_USE_SAM // Used to correct code where needed to be compatible with the Due. 231 | // #define IR_USE_DUE // Used to correctly map pins. (The idea being there might be more than one Arduino model based on SAM cores.) 232 | 233 | #include "boards/Due.h" 234 | 235 | //#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) 236 | #elif defined(ARDUINO_TEENSY30) || defined(ARDUINO_TEENSY32) 237 | // Teensy 3.0 / Teensy 3.1 / 3.2 238 | 239 | #include "boards/Teensy3x.h" 240 | 241 | #elif defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD) 242 | 243 | #include "boards/Sam.h" 244 | 245 | ///////////////////// ESP32 246 | #elif defined(ESP32) 247 | 248 | #include "boards/Esp32.h" 249 | 250 | ///////////////////// ESP8266 251 | // This board is not supported, see 252 | // https://github.com/bengtmartensson/Infrared4Arduino/issues/5 253 | // for the reasons. 254 | 255 | #else 256 | 257 | //#error Your board is currently not supported. Please add it to boarddefs.h. 258 | #warning The present board is either unknown, or does not support (Hardware) PWM \ 259 | or equidistant timer sampling. The classes IrReceiverSampler and \ 260 | IrSenderPwmHard will not be available. 261 | 262 | #include "boards/NoBoard.h" 263 | 264 | #endif 265 | 266 | //#define DEBUG_PIN 2 267 | 268 | inline void Board::setupDebugPin() { 269 | #ifdef DEBUG_PIN 270 | instance->setPinMode(DEBUG_PIN, OUTPUT); 271 | #endif 272 | } 273 | 274 | inline void Board::debugPinHigh() { 275 | #ifdef DEBUG_PIN 276 | digitalWrite(DEBUG_PIN, HIGH); 277 | #endif 278 | } 279 | 280 | inline void Board::debugPinLow() { 281 | #ifdef DEBUG_PIN 282 | digitalWrite(DEBUG_PIN, LOW); 283 | #endif 284 | } 285 | 286 | #if !HAS_FLASH_READ 287 | // Dummy definition for allowing compiling some stuff 288 | typedef void * uint_farptr_t; 289 | #endif 290 | 291 | #ifdef HAS_HARDWARE_PWM 292 | 293 | inline constexpr bool Board::hasHardwarePwm() { 294 | return true; 295 | } 296 | inline constexpr pin_t Board::defaultPwmPin() { return PWM_PIN; } 297 | inline pin_t Board::getPwmPin() const { return PWM_PIN; } 298 | inline void Board::writeLow() { digitalWrite(getPwmPin(), LOW); } 299 | inline void Board::writeHigh() { digitalWrite(getPwmPin(), HIGH); } 300 | 301 | #else 302 | 303 | inline constexpr bool Board::hasHardwarePwm() { return false; }; 304 | inline constexpr pin_t Board::defaultPwmPin() { return NO_PIN; }; 305 | inline pin_t Board::getPwmPin() const { return NO_PIN; }; 306 | inline void Board::writeLow() { digitalWrite(getPwmPin(), LOW); }; 307 | inline void Board::writeHigh() { digitalWrite(getPwmPin(), HIGH); }; 308 | 309 | #endif --------------------------------------------------------------------------------