From 6228f52a5800124de4a43adb0c05ea5b4207c18f Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Wed, 3 Jun 2015 15:06:17 +0200 Subject: Add libraries HMC5883L: licensed under GPLv3 (converted CRLF to LF). IRremote: licensed under LGPL2.1 (unmodified). --- Venus_Skeleton/libs/HMC5883L/HMC5883L.cpp | 160 ++++ Venus_Skeleton/libs/HMC5883L/HMC5883L.h | 75 ++ Venus_Skeleton/libs/IRremote/IRremote.cpp | 1241 ++++++++++++++++++++++++++++ Venus_Skeleton/libs/IRremote/IRremote.h | 130 +++ Venus_Skeleton/libs/IRremote/IRremoteInt.h | 525 ++++++++++++ 5 files changed, 2131 insertions(+) create mode 100644 Venus_Skeleton/libs/HMC5883L/HMC5883L.cpp create mode 100644 Venus_Skeleton/libs/HMC5883L/HMC5883L.h create mode 100644 Venus_Skeleton/libs/IRremote/IRremote.cpp create mode 100644 Venus_Skeleton/libs/IRremote/IRremote.h create mode 100644 Venus_Skeleton/libs/IRremote/IRremoteInt.h diff --git a/Venus_Skeleton/libs/HMC5883L/HMC5883L.cpp b/Venus_Skeleton/libs/HMC5883L/HMC5883L.cpp new file mode 100644 index 0000000..68ebd02 --- /dev/null +++ b/Venus_Skeleton/libs/HMC5883L/HMC5883L.cpp @@ -0,0 +1,160 @@ +/* +HMC5883L.cpp - Class file for the HMC5883L Triple Axis Magnetometer Arduino Library. + +This program is free software: you can redistribute it and/or modify +it under the terms of the version 3 GNU General Public License as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + WARNING: THE HMC5883L IS NOT IDENTICAL TO THE HMC5883! + Datasheet for HMC5883L: + http://www51.honeywell.com/aero/common/documents/myaerospacecatalog-documents/Defense_Brochures-documents/HMC5883L_3-Axis_Digital_Compass_IC.pdf + +*/ +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif +#include "HMC5883L.h" + +HMC5883L::HMC5883L() +{ + m_Scale = 1; +} + +MagnetometerRaw HMC5883L::ReadRawAxis() +{ + uint8_t* buffer = Read(DataRegisterBegin, 6); + MagnetometerRaw raw = MagnetometerRaw(); + raw.XAxis = (buffer[0] << 8) | buffer[1]; + raw.ZAxis = (buffer[2] << 8) | buffer[3]; + raw.YAxis = (buffer[4] << 8) | buffer[5]; + return raw; +} + +MagnetometerScaled HMC5883L::ReadScaledAxis() +{ + MagnetometerRaw raw = ReadRawAxis(); + MagnetometerScaled scaled = MagnetometerScaled(); + scaled.XAxis = raw.XAxis * m_Scale; + scaled.ZAxis = raw.ZAxis * m_Scale; + scaled.YAxis = raw.YAxis * m_Scale; + return scaled; +} + +int HMC5883L::SetScale(float gauss) +{ + uint8_t regValue = 0x00; + if(gauss == 0.88) + { + regValue = 0x00; + m_Scale = 0.73; + } + else if(gauss == 1.3) + { + regValue = 0x01; + m_Scale = 0.92; + } + else if(gauss == 1.9) + { + regValue = 0x02; + m_Scale = 1.22; + } + else if(gauss == 2.5) + { + regValue = 0x03; + m_Scale = 1.52; + } + else if(gauss == 4.0) + { + regValue = 0x04; + m_Scale = 2.27; + } + else if(gauss == 4.7) + { + regValue = 0x05; + m_Scale = 2.56; + } + else if(gauss == 5.6) + { + regValue = 0x06; + m_Scale = 3.03; + } + else if(gauss == 8.1) + { + regValue = 0x07; + m_Scale = 4.35; + } + else + return ErrorCode_1_Num; + + // Setting is in the top 3 bits of the register. + regValue = regValue << 5; + Write(ConfigurationRegisterB, regValue); +} + +int HMC5883L::SetMeasurementMode(uint8_t mode) +{ + Write(ModeRegister, mode); + return 0; +} + +void HMC5883L::Write(int address, int data) +{ + Wire.beginTransmission(HMC5883L_Address); + #if ARDUINO >= 100 + Wire.write((uint8_t)address); + Wire.write((uint8_t)data); + #else + Wire.send(address); + Wire.send(data); + #endif + + Wire.endTransmission(); +} + +uint8_t* HMC5883L::Read(int address, int length) +{ + Wire.beginTransmission(HMC5883L_Address); +#if ARDUINO >= 100 + Wire.write((uint8_t)address); +#else + Wire.send(address); +#endif + Wire.endTransmission(); + + Wire.beginTransmission(HMC5883L_Address); + Wire.requestFrom(HMC5883L_Address, length); + + uint8_t buffer[length]; + if(Wire.available() == length) + { + for(uint8_t i = 0; i < length; i++) + { + #if ARDUINO >= 100 + buffer[i] = Wire.read(); + #else + buffer[i] = Wire.receive(); + #endif + } + } + Wire.endTransmission(); + + return buffer; +} + +char* HMC5883L::GetErrorText(int errorCode) +{ + if (errorCode == ErrorCode_1_Num) + return ErrorCode_1; + + return "Error not defined."; +} \ No newline at end of file diff --git a/Venus_Skeleton/libs/HMC5883L/HMC5883L.h b/Venus_Skeleton/libs/HMC5883L/HMC5883L.h new file mode 100644 index 0000000..64ac68c --- /dev/null +++ b/Venus_Skeleton/libs/HMC5883L/HMC5883L.h @@ -0,0 +1,75 @@ +/* +HMC5883L.h - Header file for the HMC5883L Triple Axis Magnetometer Arduino Library. + +This program is free software: you can redistribute it and/or modify +it under the terms of the version 3 GNU General Public License as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + WARNING: THE HMC5883L IS NOT IDENTICAL TO THE HMC5883! + Datasheet for HMC5883L: + http://www51.honeywell.com/aero/common/documents/myaerospacecatalog-documents/Defense_Brochures-documents/HMC5883L_3-Axis_Digital_Compass_IC.pdf + +*/ + +#ifndef HMC5883L_h +#define HMC5883L_h + +#include +#include "../Wire/Wire.h" + +#define HMC5883L_Address 0x1E +#define ConfigurationRegisterA 0x00 +#define ConfigurationRegisterB 0x01 +#define ModeRegister 0x02 +#define DataRegisterBegin 0x03 + +#define Measurement_Continuous 0x00 +#define Measurement_SingleShot 0x01 +#define Measurement_Idle 0x03 + +#define ErrorCode_1 "Entered scale was not valid, valid gauss values are: 0.88, 1.3, 1.9, 2.5, 4.0, 4.7, 5.6, 8.1" +#define ErrorCode_1_Num 1 + +struct MagnetometerScaled +{ + float XAxis; + float YAxis; + float ZAxis; +}; + +struct MagnetometerRaw +{ + int XAxis; + int YAxis; + int ZAxis; +}; + +class HMC5883L +{ + public: + HMC5883L(); + + MagnetometerRaw ReadRawAxis(); + MagnetometerScaled ReadScaledAxis(); + + int SetMeasurementMode(uint8_t mode); + int SetScale(float gauss); + + char* GetErrorText(int errorCode); + + protected: + void Write(int address, int byte); + uint8_t* Read(int address, int length); + + private: + float m_Scale; +}; +#endif \ No newline at end of file diff --git a/Venus_Skeleton/libs/IRremote/IRremote.cpp b/Venus_Skeleton/libs/IRremote/IRremote.cpp new file mode 100644 index 0000000..af5a3c8 --- /dev/null +++ b/Venus_Skeleton/libs/IRremote/IRremote.cpp @@ -0,0 +1,1241 @@ +/* + * IRremote + * Version 0.11 August, 2009 + * Copyright 2009 Ken Shirriff + * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html + * + * Modified by Paul Stoffregen to support other boards and timers + * Modified by Mitra Ardron + * Added Sanyo and Mitsubishi controllers + * Modified Sony to spot the repeat codes that some Sony's send + * + * Interrupt code based on NECIRrcv by Joe Knapp + * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 + * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ + * + * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) + * LG added by Darryl Smith (based on the JVC protocol) + * Whynter A/C ARC-110WD added by Francesco Meschia + */ + +#include "IRremote.h" +#include "IRremoteInt.h" + +// Provides ISR +#include + +volatile irparams_t irparams; + +// These versions of MATCH, MATCH_MARK, and MATCH_SPACE are only for debugging. +// To use them, set DEBUG in IRremoteInt.h +// Normally macros are used for efficiency +#ifdef DEBUG +int MATCH(int measured, int desired) { + Serial.print("Testing: "); + Serial.print(TICKS_LOW(desired), DEC); + Serial.print(" <= "); + Serial.print(measured, DEC); + Serial.print(" <= "); + Serial.println(TICKS_HIGH(desired), DEC); + return measured >= TICKS_LOW(desired) && measured <= TICKS_HIGH(desired); +} + +int MATCH_MARK(int measured_ticks, int desired_us) { + Serial.print("Testing mark "); + Serial.print(measured_ticks * USECPERTICK, DEC); + Serial.print(" vs "); + Serial.print(desired_us, DEC); + Serial.print(": "); + Serial.print(TICKS_LOW(desired_us + MARK_EXCESS), DEC); + Serial.print(" <= "); + Serial.print(measured_ticks, DEC); + Serial.print(" <= "); + Serial.println(TICKS_HIGH(desired_us + MARK_EXCESS), DEC); + return measured_ticks >= TICKS_LOW(desired_us + MARK_EXCESS) && measured_ticks <= TICKS_HIGH(desired_us + MARK_EXCESS); +} + +int MATCH_SPACE(int measured_ticks, int desired_us) { + Serial.print("Testing space "); + Serial.print(measured_ticks * USECPERTICK, DEC); + Serial.print(" vs "); + Serial.print(desired_us, DEC); + Serial.print(": "); + Serial.print(TICKS_LOW(desired_us - MARK_EXCESS), DEC); + Serial.print(" <= "); + Serial.print(measured_ticks, DEC); + Serial.print(" <= "); + Serial.println(TICKS_HIGH(desired_us - MARK_EXCESS), DEC); + return measured_ticks >= TICKS_LOW(desired_us - MARK_EXCESS) && measured_ticks <= TICKS_HIGH(desired_us - MARK_EXCESS); +} +#else +int MATCH(int measured, int desired) {return measured >= TICKS_LOW(desired) && measured <= TICKS_HIGH(desired);} +int MATCH_MARK(int measured_ticks, int desired_us) {return MATCH(measured_ticks, (desired_us + MARK_EXCESS));} +int MATCH_SPACE(int measured_ticks, int desired_us) {return MATCH(measured_ticks, (desired_us - MARK_EXCESS));} +// Debugging versions are in IRremote.cpp +#endif + +void IRsend::sendNEC(unsigned long data, int nbits) +{ + enableIROut(38); + mark(NEC_HDR_MARK); + space(NEC_HDR_SPACE); + for (int i = 0; i < nbits; i++) { + if (data & TOPBIT) { + mark(NEC_BIT_MARK); + space(NEC_ONE_SPACE); + } + else { + mark(NEC_BIT_MARK); + space(NEC_ZERO_SPACE); + } + data <<= 1; + } + mark(NEC_BIT_MARK); + space(0); +} + +void IRsend::sendWhynter(unsigned long data, int nbits) { + enableIROut(38); + mark(WHYNTER_ZERO_MARK); + space(WHYNTER_ZERO_SPACE); + mark(WHYNTER_HDR_MARK); + space(WHYNTER_HDR_SPACE); + for (int i = 0; i < nbits; i++) { + if (data & TOPBIT) { + mark(WHYNTER_ONE_MARK); + space(WHYNTER_ONE_SPACE); + } + else { + mark(WHYNTER_ZERO_MARK); + space(WHYNTER_ZERO_SPACE); + } + data <<= 1; + } + mark(WHYNTER_ZERO_MARK); + space(WHYNTER_ZERO_SPACE); +} + +void IRsend::sendSony(unsigned long data, int nbits) { + enableIROut(40); + mark(SONY_HDR_MARK); + space(SONY_HDR_SPACE); + data = data << (32 - nbits); + for (int i = 0; i < nbits; i++) { + if (data & TOPBIT) { + mark(SONY_ONE_MARK); + space(SONY_HDR_SPACE); + } + else { + mark(SONY_ZERO_MARK); + space(SONY_HDR_SPACE); + } + data <<= 1; + } +} + +void IRsend::sendRaw(unsigned int buf[], int len, int hz) +{ + enableIROut(hz); + for (int i = 0; i < len; i++) { + if (i & 1) { + space(buf[i]); + } + else { + mark(buf[i]); + } + } + space(0); // Just to be sure +} + +// Note: first bit must be a one (start bit) +void IRsend::sendRC5(unsigned long data, int nbits) +{ + enableIROut(36); + data = data << (32 - nbits); + mark(RC5_T1); // First start bit + space(RC5_T1); // Second start bit + mark(RC5_T1); // Second start bit + for (int i = 0; i < nbits; i++) { + if (data & TOPBIT) { + space(RC5_T1); // 1 is space, then mark + mark(RC5_T1); + } + else { + mark(RC5_T1); + space(RC5_T1); + } + data <<= 1; + } + space(0); // Turn off at end +} + +// Caller needs to take care of flipping the toggle bit +void IRsend::sendRC6(unsigned long data, int nbits) +{ + enableIROut(36); + data = data << (32 - nbits); + mark(RC6_HDR_MARK); + space(RC6_HDR_SPACE); + mark(RC6_T1); // start bit + space(RC6_T1); + int t; + for (int i = 0; i < nbits; i++) { + if (i == 3) { + // double-wide trailer bit + t = 2 * RC6_T1; + } + else { + t = RC6_T1; + } + if (data & TOPBIT) { + mark(t); + space(t); + } + else { + space(t); + mark(t); + } + + data <<= 1; + } + space(0); // Turn off at end +} +void IRsend::sendPanasonic(unsigned int address, unsigned long data) { + enableIROut(35); + mark(PANASONIC_HDR_MARK); + space(PANASONIC_HDR_SPACE); + + for(int i=0;i<16;i++) + { + mark(PANASONIC_BIT_MARK); + if (address & 0x8000) { + space(PANASONIC_ONE_SPACE); + } else { + space(PANASONIC_ZERO_SPACE); + } + address <<= 1; + } + for (int i=0; i < 32; i++) { + mark(PANASONIC_BIT_MARK); + if (data & TOPBIT) { + space(PANASONIC_ONE_SPACE); + } else { + space(PANASONIC_ZERO_SPACE); + } + data <<= 1; + } + mark(PANASONIC_BIT_MARK); + space(0); +} +void IRsend::sendJVC(unsigned long data, int nbits, int repeat) +{ + enableIROut(38); + data = data << (32 - nbits); + if (!repeat){ + mark(JVC_HDR_MARK); + space(JVC_HDR_SPACE); + } + for (int i = 0; i < nbits; i++) { + if (data & TOPBIT) { + mark(JVC_BIT_MARK); + space(JVC_ONE_SPACE); + } + else { + mark(JVC_BIT_MARK); + space(JVC_ZERO_SPACE); + } + data <<= 1; + } + mark(JVC_BIT_MARK); + space(0); +} + +void IRsend::sendSAMSUNG(unsigned long data, int nbits) +{ + enableIROut(38); + mark(SAMSUNG_HDR_MARK); + space(SAMSUNG_HDR_SPACE); + for (int i = 0; i < nbits; i++) { + if (data & TOPBIT) { + mark(SAMSUNG_BIT_MARK); + space(SAMSUNG_ONE_SPACE); + } + else { + mark(SAMSUNG_BIT_MARK); + space(SAMSUNG_ZERO_SPACE); + } + data <<= 1; + } + mark(SAMSUNG_BIT_MARK); + space(0); +} + +void IRsend::mark(int time) { + // Sends an IR mark for the specified number of microseconds. + // The mark output is modulated at the PWM frequency. + TIMER_ENABLE_PWM; // Enable pin 3 PWM output + if (time > 0) delayMicroseconds(time); +} + +/* Leave pin off for time (given in microseconds) */ +void IRsend::space(int time) { + // Sends an IR space for the specified number of microseconds. + // A space is no output, so the PWM output is disabled. + TIMER_DISABLE_PWM; // Disable pin 3 PWM output + if (time > 0) delayMicroseconds(time); +} + +void IRsend::enableIROut(int khz) { + // Enables IR output. The khz value controls the modulation frequency in kilohertz. + // The IR output will be on pin 3 (OC2B). + // This routine is designed for 36-40KHz; if you use it for other values, it's up to you + // to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.) + // TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B + // controlling the duty cycle. + // There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A) + // To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin. + // A few hours staring at the ATmega documentation and this will all make sense. + // See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details. + + + // Disable the Timer2 Interrupt (which is used for receiving IR) + TIMER_DISABLE_INTR; //Timer2 Overflow Interrupt + + pinMode(TIMER_PWM_PIN, OUTPUT); + digitalWrite(TIMER_PWM_PIN, LOW); // When not sending PWM, we want it low + + // COM2A = 00: disconnect OC2A + // COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted + // WGM2 = 101: phase-correct PWM with OCRA as top + // CS2 = 000: no prescaling + // The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A. + TIMER_CONFIG_KHZ(khz); +} + +IRrecv::IRrecv(int recvpin) +{ + irparams.recvpin = recvpin; + irparams.blinkflag = 0; +} + +// initialization +void IRrecv::enableIRIn() { + cli(); + // setup pulse clock timer interrupt + //Prescale /8 (16M/8 = 0.5 microseconds per tick) + // Therefore, the timer interval can range from 0.5 to 128 microseconds + // depending on the reset value (255 to 0) + TIMER_CONFIG_NORMAL(); + + //Timer2 Overflow Interrupt Enable + TIMER_ENABLE_INTR; + + TIMER_RESET; + + sei(); // enable interrupts + + // initialize state machine variables + irparams.rcvstate = STATE_IDLE; + irparams.rawlen = 0; + + // set pin modes + pinMode(irparams.recvpin, INPUT); +} + +// enable/disable blinking of pin 13 on IR processing +void IRrecv::blink13(int blinkflag) +{ + irparams.blinkflag = blinkflag; + if (blinkflag) + pinMode(BLINKLED, OUTPUT); +} + +// TIMER2 interrupt code to collect raw data. +// Widths of alternating SPACE, MARK are recorded in rawbuf. +// Recorded in ticks of 50 microseconds. +// rawlen counts the number of entries recorded so far. +// First entry is the SPACE between transmissions. +// As soon as a SPACE gets long, ready is set, state switches to IDLE, timing of SPACE continues. +// As soon as first MARK arrives, gap width is recorded, ready is cleared, and new logging starts +ISR(TIMER_INTR_NAME) +{ + TIMER_RESET; + + uint8_t irdata = (uint8_t)digitalRead(irparams.recvpin); + + irparams.timer++; // One more 50us tick + if (irparams.rawlen >= RAWBUF) { + // Buffer overflow + irparams.rcvstate = STATE_STOP; + } + switch(irparams.rcvstate) { + case STATE_IDLE: // In the middle of a gap + if (irdata == MARK) { + if (irparams.timer < GAP_TICKS) { + // Not big enough to be a gap. + irparams.timer = 0; + } + else { + // gap just ended, record duration and start recording transmission + irparams.rawlen = 0; + irparams.rawbuf[irparams.rawlen++] = irparams.timer; + irparams.timer = 0; + irparams.rcvstate = STATE_MARK; + } + } + break; + case STATE_MARK: // timing MARK + if (irdata == SPACE) { // MARK ended, record time + irparams.rawbuf[irparams.rawlen++] = irparams.timer; + irparams.timer = 0; + irparams.rcvstate = STATE_SPACE; + } + break; + case STATE_SPACE: // timing SPACE + if (irdata == MARK) { // SPACE just ended, record it + irparams.rawbuf[irparams.rawlen++] = irparams.timer; + irparams.timer = 0; + irparams.rcvstate = STATE_MARK; + } + else { // SPACE + if (irparams.timer > GAP_TICKS) { + // big SPACE, indicates gap between codes + // Mark current code as ready for processing + // Switch to STOP + // Don't reset timer; keep counting space width + irparams.rcvstate = STATE_STOP; + } + } + break; + case STATE_STOP: // waiting, measuring gap + if (irdata == MARK) { // reset gap timer + irparams.timer = 0; + } + break; + } + + if (irparams.blinkflag) { + if (irdata == MARK) { + BLINKLED_ON(); // turn pin 13 LED on + } + else { + BLINKLED_OFF(); // turn pin 13 LED off + } + } +} + +void IRrecv::resume() { + irparams.rcvstate = STATE_IDLE; + irparams.rawlen = 0; +} + + + +// Decodes the received IR message +// Returns 0 if no data ready, 1 if data ready. +// Results of decoding are stored in results +int IRrecv::decode(decode_results *results) { + results->rawbuf = irparams.rawbuf; + results->rawlen = irparams.rawlen; + if (irparams.rcvstate != STATE_STOP) { + return ERR; + } +#ifdef DEBUG + Serial.println("Attempting NEC decode"); +#endif + if (decodeNEC(results)) { + return DECODED; + } +#ifdef DEBUG + Serial.println("Attempting Sony decode"); +#endif + if (decodeSony(results)) { + return DECODED; + } +#ifdef DEBUG + Serial.println("Attempting Sanyo decode"); +#endif + if (decodeSanyo(results)) { + return DECODED; + } +#ifdef DEBUG + Serial.println("Attempting Mitsubishi decode"); +#endif + if (decodeMitsubishi(results)) { + return DECODED; + } +#ifdef DEBUG + Serial.println("Attempting RC5 decode"); +#endif + if (decodeRC5(results)) { + return DECODED; + } +#ifdef DEBUG + Serial.println("Attempting RC6 decode"); +#endif + if (decodeRC6(results)) { + return DECODED; + } +#ifdef DEBUG + Serial.println("Attempting Panasonic decode"); +#endif + if (decodePanasonic(results)) { + return DECODED; + } +#ifdef DEBUG + Serial.println("Attempting LG decode"); +#endif + if (decodeLG(results)) { + return DECODED; + } +#ifdef DEBUG + Serial.println("Attempting JVC decode"); +#endif + if (decodeJVC(results)) { + return DECODED; + } +#ifdef DEBUG + Serial.println("Attempting SAMSUNG decode"); +#endif + if (decodeSAMSUNG(results)) { + return DECODED; + } +#ifdef DEBUG + Serial.println("Attempting Whynter decode"); +#endif + if (decodeWhynter(results)) { + return DECODED; + } + // decodeHash returns a hash on any input. + // Thus, it needs to be last in the list. + // If you add any decodes, add them before this. + if (decodeHash(results)) { + return DECODED; + } + // Throw away and start over + resume(); + return ERR; +} + +// NECs have a repeat only 4 items long +long IRrecv::decodeNEC(decode_results *results) { + long data = 0; + int offset = 1; // Skip first space + // Initial mark + if (!MATCH_MARK(results->rawbuf[offset], NEC_HDR_MARK)) { + return ERR; + } + offset++; + // Check for repeat + if (irparams.rawlen == 4 && + MATCH_SPACE(results->rawbuf[offset], NEC_RPT_SPACE) && + MATCH_MARK(results->rawbuf[offset+1], NEC_BIT_MARK)) { + results->bits = 0; + results->value = REPEAT; + results->decode_type = NEC; + return DECODED; + } + if (irparams.rawlen < 2 * NEC_BITS + 4) { + return ERR; + } + // Initial space + if (!MATCH_SPACE(results->rawbuf[offset], NEC_HDR_SPACE)) { + return ERR; + } + offset++; + for (int i = 0; i < NEC_BITS; i++) { + if (!MATCH_MARK(results->rawbuf[offset], NEC_BIT_MARK)) { + return ERR; + } + offset++; + if (MATCH_SPACE(results->rawbuf[offset], NEC_ONE_SPACE)) { + data = (data << 1) | 1; + } + else if (MATCH_SPACE(results->rawbuf[offset], NEC_ZERO_SPACE)) { + data <<= 1; + } + else { + return ERR; + } + offset++; + } + // Success + results->bits = NEC_BITS; + results->value = data; + results->decode_type = NEC; + return DECODED; +} + +long IRrecv::decodeSony(decode_results *results) { + long data = 0; + if (irparams.rawlen < 2 * SONY_BITS + 2) { + return ERR; + } + int offset = 0; // Dont skip first space, check its size + + // Some Sony's deliver repeats fast after first + // unfortunately can't spot difference from of repeat from two fast clicks + if (results->rawbuf[offset] < SONY_DOUBLE_SPACE_USECS) { + // Serial.print("IR Gap found: "); + results->bits = 0; + results->value = REPEAT; + results->decode_type = SANYO; + return DECODED; + } + offset++; + + // Initial mark + if (!MATCH_MARK(results->rawbuf[offset], SONY_HDR_MARK)) { + return ERR; + } + offset++; + + while (offset + 1 < irparams.rawlen) { + if (!MATCH_SPACE(results->rawbuf[offset], SONY_HDR_SPACE)) { + break; + } + offset++; + if (MATCH_MARK(results->rawbuf[offset], SONY_ONE_MARK)) { + data = (data << 1) | 1; + } + else if (MATCH_MARK(results->rawbuf[offset], SONY_ZERO_MARK)) { + data <<= 1; + } + else { + return ERR; + } + offset++; + } + + // Success + results->bits = (offset - 1) / 2; + if (results->bits < 12) { + results->bits = 0; + return ERR; + } + results->value = data; + results->decode_type = SONY; + return DECODED; +} + +long IRrecv::decodeWhynter(decode_results *results) { + long data = 0; + + if (irparams.rawlen < 2 * WHYNTER_BITS + 6) { + return ERR; + } + + int offset = 1; // skip initial space + + // sequence begins with a bit mark and a zero space + if (!MATCH_MARK(results->rawbuf[offset], WHYNTER_BIT_MARK)) { + return ERR; + } + offset++; + if (!MATCH_SPACE(results->rawbuf[offset], WHYNTER_ZERO_SPACE)) { + return ERR; + } + offset++; + + // header mark and space + if (!MATCH_MARK(results->rawbuf[offset], WHYNTER_HDR_MARK)) { + return ERR; + } + offset++; + if (!MATCH_SPACE(results->rawbuf[offset], WHYNTER_HDR_SPACE)) { + return ERR; + } + offset++; + + // data bits + for (int i = 0; i < WHYNTER_BITS; i++) { + if (!MATCH_MARK(results->rawbuf[offset], WHYNTER_BIT_MARK)) { + return ERR; + } + offset++; + if (MATCH_SPACE(results->rawbuf[offset], WHYNTER_ONE_SPACE)) { + data = (data << 1) | 1; + } + else if (MATCH_SPACE(results->rawbuf[offset],WHYNTER_ZERO_SPACE)) { + data <<= 1; + } + else { + return ERR; + } + offset++; + } + + // trailing mark + if (!MATCH_MARK(results->rawbuf[offset], WHYNTER_BIT_MARK)) { + return ERR; + } + // Success + results->bits = WHYNTER_BITS; + results->value = data; + results->decode_type = WHYNTER; + return DECODED; +} + + +// I think this is a Sanyo decoder - serial = SA 8650B +// Looks like Sony except for timings, 48 chars of data and time/space different +long IRrecv::decodeSanyo(decode_results *results) { + long data = 0; + if (irparams.rawlen < 2 * SANYO_BITS + 2) { + return ERR; + } + int offset = 0; // Skip first space + // Initial space + /* Put this back in for debugging - note can't use #DEBUG as if Debug on we don't see the repeat cos of the delay + Serial.print("IR Gap: "); + Serial.println( results->rawbuf[offset]); + Serial.println( "test against:"); + Serial.println(results->rawbuf[offset]); + */ + if (results->rawbuf[offset] < SANYO_DOUBLE_SPACE_USECS) { + // Serial.print("IR Gap found: "); + results->bits = 0; + results->value = REPEAT; + results->decode_type = SANYO; + return DECODED; + } + offset++; + + // Initial mark + if (!MATCH_MARK(results->rawbuf[offset], SANYO_HDR_MARK)) { + return ERR; + } + offset++; + + // Skip Second Mark + if (!MATCH_MARK(results->rawbuf[offset], SANYO_HDR_MARK)) { + return ERR; + } + offset++; + + while (offset + 1 < irparams.rawlen) { + if (!MATCH_SPACE(results->rawbuf[offset], SANYO_HDR_SPACE)) { + break; + } + offset++; + if (MATCH_MARK(results->rawbuf[offset], SANYO_ONE_MARK)) { + data = (data << 1) | 1; + } + else if (MATCH_MARK(results->rawbuf[offset], SANYO_ZERO_MARK)) { + data <<= 1; + } + else { + return ERR; + } + offset++; + } + + // Success + results->bits = (offset - 1) / 2; + if (results->bits < 12) { + results->bits = 0; + return ERR; + } + results->value = data; + results->decode_type = SANYO; + return DECODED; +} + +// Looks like Sony except for timings, 48 chars of data and time/space different +long IRrecv::decodeMitsubishi(decode_results *results) { + // Serial.print("?!? decoding Mitsubishi:");Serial.print(irparams.rawlen); Serial.print(" want "); Serial.println( 2 * MITSUBISHI_BITS + 2); + long data = 0; + if (irparams.rawlen < 2 * MITSUBISHI_BITS + 2) { + return ERR; + } + int offset = 0; // Skip first space + // Initial space + /* Put this back in for debugging - note can't use #DEBUG as if Debug on we don't see the repeat cos of the delay + Serial.print("IR Gap: "); + Serial.println( results->rawbuf[offset]); + Serial.println( "test against:"); + Serial.println(results->rawbuf[offset]); + */ + /* Not seeing double keys from Mitsubishi + if (results->rawbuf[offset] < MITSUBISHI_DOUBLE_SPACE_USECS) { + // Serial.print("IR Gap found: "); + results->bits = 0; + results->value = REPEAT; + results->decode_type = MITSUBISHI; + return DECODED; + } + */ + offset++; + + // Typical + // 14200 7 41 7 42 7 42 7 17 7 17 7 18 7 41 7 18 7 17 7 17 7 18 7 41 8 17 7 17 7 18 7 17 7 + + // Initial Space + if (!MATCH_MARK(results->rawbuf[offset], MITSUBISHI_HDR_SPACE)) { + return ERR; + } + offset++; + while (offset + 1 < irparams.rawlen) { + if (MATCH_MARK(results->rawbuf[offset], MITSUBISHI_ONE_MARK)) { + data = (data << 1) | 1; + } + else if (MATCH_MARK(results->rawbuf[offset], MITSUBISHI_ZERO_MARK)) { + data <<= 1; + } + else { + // Serial.println("A"); Serial.println(offset); Serial.println(results->rawbuf[offset]); + return ERR; + } + offset++; + if (!MATCH_SPACE(results->rawbuf[offset], MITSUBISHI_HDR_SPACE)) { + // Serial.println("B"); Serial.println(offset); Serial.println(results->rawbuf[offset]); + break; + } + offset++; + } + + // Success + results->bits = (offset - 1) / 2; + if (results->bits < MITSUBISHI_BITS) { + results->bits = 0; + return ERR; + } + results->value = data; + results->decode_type = MITSUBISHI; + return DECODED; +} + + +// Gets one undecoded level at a time from the raw buffer. +// The RC5/6 decoding is easier if the data is broken into time intervals. +// E.g. if the buffer has MARK for 2 time intervals and SPACE for 1, +// successive calls to getRClevel will return MARK, MARK, SPACE. +// offset and used are updated to keep track of the current position. +// t1 is the time interval for a single bit in microseconds. +// Returns -1 for error (measured time interval is not a multiple of t1). +int IRrecv::getRClevel(decode_results *results, int *offset, int *used, int t1) { + if (*offset >= results->rawlen) { + // After end of recorded buffer, assume SPACE. + return SPACE; + } + int width = results->rawbuf[*offset]; + int val = ((*offset) % 2) ? MARK : SPACE; + int correction = (val == MARK) ? MARK_EXCESS : - MARK_EXCESS; + + int avail; + if (MATCH(width, t1 + correction)) { + avail = 1; + } + else if (MATCH(width, 2*t1 + correction)) { + avail = 2; + } + else if (MATCH(width, 3*t1 + correction)) { + avail = 3; + } + else { + return -1; + } + + (*used)++; + if (*used >= avail) { + *used = 0; + (*offset)++; + } +#ifdef DEBUG + if (val == MARK) { + Serial.println("MARK"); + } + else { + Serial.println("SPACE"); + } +#endif + return val; +} + +long IRrecv::decodeRC5(decode_results *results) { + if (irparams.rawlen < MIN_RC5_SAMPLES + 2) { + return ERR; + } + int offset = 1; // Skip gap space + long data = 0; + int used = 0; + // Get start bits + if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return ERR; + if (getRClevel(results, &offset, &used, RC5_T1) != SPACE) return ERR; + if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return ERR; + int nbits; + for (nbits = 0; offset < irparams.rawlen; nbits++) { + int levelA = getRClevel(results, &offset, &used, RC5_T1); + int levelB = getRClevel(results, &offset, &used, RC5_T1); + if (levelA == SPACE && levelB == MARK) { + // 1 bit + data = (data << 1) | 1; + } + else if (levelA == MARK && levelB == SPACE) { + // zero bit + data <<= 1; + } + else { + return ERR; + } + } + + // Success + results->bits = nbits; + results->value = data; + results->decode_type = RC5; + return DECODED; +} + +long IRrecv::decodeRC6(decode_results *results) { + if (results->rawlen < MIN_RC6_SAMPLES) { + return ERR; + } + int offset = 1; // Skip first space + // Initial mark + if (!MATCH_MARK(results->rawbuf[offset], RC6_HDR_MARK)) { + return ERR; + } + offset++; + if (!MATCH_SPACE(results->rawbuf[offset], RC6_HDR_SPACE)) { + return ERR; + } + offset++; + long data = 0; + int used = 0; + // Get start bit (1) + if (getRClevel(results, &offset, &used, RC6_T1) != MARK) return ERR; + if (getRClevel(results, &offset, &used, RC6_T1) != SPACE) return ERR; + int nbits; + for (nbits = 0; offset < results->rawlen; nbits++) { + int levelA, levelB; // Next two levels + levelA = getRClevel(results, &offset, &used, RC6_T1); + if (nbits == 3) { + // T bit is double wide; make sure second half matches + if (levelA != getRClevel(results, &offset, &used, RC6_T1)) return ERR; + } + levelB = getRClevel(results, &offset, &used, RC6_T1); + if (nbits == 3) { + // T bit is double wide; make sure second half matches + if (levelB != getRClevel(results, &offset, &used, RC6_T1)) return ERR; + } + if (levelA == MARK && levelB == SPACE) { // reversed compared to RC5 + // 1 bit + data = (data << 1) | 1; + } + else if (levelA == SPACE && levelB == MARK) { + // zero bit + data <<= 1; + } + else { + return ERR; // Error + } + } + // Success + results->bits = nbits; + results->value = data; + results->decode_type = RC6; + return DECODED; +} +long IRrecv::decodePanasonic(decode_results *results) { + unsigned long long data = 0; + int offset = 1; + + if (!MATCH_MARK(results->rawbuf[offset], PANASONIC_HDR_MARK)) { + return ERR; + } + offset++; + if (!MATCH_MARK(results->rawbuf[offset], PANASONIC_HDR_SPACE)) { + return ERR; + } + offset++; + + // decode address + for (int i = 0; i < PANASONIC_BITS; i++) { + if (!MATCH_MARK(results->rawbuf[offset++], PANASONIC_BIT_MARK)) { + return ERR; + } + if (MATCH_SPACE(results->rawbuf[offset],PANASONIC_ONE_SPACE)) { + data = (data << 1) | 1; + } else if (MATCH_SPACE(results->rawbuf[offset],PANASONIC_ZERO_SPACE)) { + data <<= 1; + } else { + return ERR; + } + offset++; + } + results->value = (unsigned long)data; + results->panasonicAddress = (unsigned int)(data >> 32); + results->decode_type = PANASONIC; + results->bits = PANASONIC_BITS; + return DECODED; +} + +long IRrecv::decodeLG(decode_results *results) { + long data = 0; + int offset = 1; // Skip first space + + // Initial mark + if (!MATCH_MARK(results->rawbuf[offset], LG_HDR_MARK)) { + return ERR; + } + offset++; + if (irparams.rawlen < 2 * LG_BITS + 1 ) { + return ERR; + } + // Initial space + if (!MATCH_SPACE(results->rawbuf[offset], LG_HDR_SPACE)) { + return ERR; + } + offset++; + for (int i = 0; i < LG_BITS; i++) { + if (!MATCH_MARK(results->rawbuf[offset], LG_BIT_MARK)) { + return ERR; + } + offset++; + if (MATCH_SPACE(results->rawbuf[offset], LG_ONE_SPACE)) { + data = (data << 1) | 1; + } + else if (MATCH_SPACE(results->rawbuf[offset], LG_ZERO_SPACE)) { + data <<= 1; + } + else { + return ERR; + } + offset++; + } + //Stop bit + if (!MATCH_MARK(results->rawbuf[offset], LG_BIT_MARK)){ + return ERR; + } + // Success + results->bits = LG_BITS; + results->value = data; + results->decode_type = LG; + return DECODED; +} + + +long IRrecv::decodeJVC(decode_results *results) { + long data = 0; + int offset = 1; // Skip first space + // Check for repeat + if (irparams.rawlen - 1 == 33 && + MATCH_MARK(results->rawbuf[offset], JVC_BIT_MARK) && + MATCH_MARK(results->rawbuf[irparams.rawlen-1], JVC_BIT_MARK)) { + results->bits = 0; + results->value = REPEAT; + results->decode_type = JVC; + return DECODED; + } + // Initial mark + if (!MATCH_MARK(results->rawbuf[offset], JVC_HDR_MARK)) { + return ERR; + } + offset++; + if (irparams.rawlen < 2 * JVC_BITS + 1 ) { + return ERR; + } + // Initial space + if (!MATCH_SPACE(results->rawbuf[offset], JVC_HDR_SPACE)) { + return ERR; + } + offset++; + for (int i = 0; i < JVC_BITS; i++) { + if (!MATCH_MARK(results->rawbuf[offset], JVC_BIT_MARK)) { + return ERR; + } + offset++; + if (MATCH_SPACE(results->rawbuf[offset], JVC_ONE_SPACE)) { + data = (data << 1) | 1; + } + else if (MATCH_SPACE(results->rawbuf[offset], JVC_ZERO_SPACE)) { + data <<= 1; + } + else { + return ERR; + } + offset++; + } + //Stop bit + if (!MATCH_MARK(results->rawbuf[offset], JVC_BIT_MARK)){ + return ERR; + } + // Success + results->bits = JVC_BITS; + results->value = data; + results->decode_type = JVC; + return DECODED; +} + +// SAMSUNGs have a repeat only 4 items long +long IRrecv::decodeSAMSUNG(decode_results *results) { + long data = 0; + int offset = 1; // Skip first space + // Initial mark + if (!MATCH_MARK(results->rawbuf[offset], SAMSUNG_HDR_MARK)) { + return ERR; + } + offset++; + // Check for repeat + if (irparams.rawlen == 4 && + MATCH_SPACE(results->rawbuf[offset], SAMSUNG_RPT_SPACE) && + MATCH_MARK(results->rawbuf[offset+1], SAMSUNG_BIT_MARK)) { + results->bits = 0; + results->value = REPEAT; + results->decode_type = SAMSUNG; + return DECODED; + } + if (irparams.rawlen < 2 * SAMSUNG_BITS + 4) { + return ERR; + } + // Initial space + if (!MATCH_SPACE(results->rawbuf[offset], SAMSUNG_HDR_SPACE)) { + return ERR; + } + offset++; + for (int i = 0; i < SAMSUNG_BITS; i++) { + if (!MATCH_MARK(results->rawbuf[offset], SAMSUNG_BIT_MARK)) { + return ERR; + } + offset++; + if (MATCH_SPACE(results->rawbuf[offset], SAMSUNG_ONE_SPACE)) { + data = (data << 1) | 1; + } + else if (MATCH_SPACE(results->rawbuf[offset], SAMSUNG_ZERO_SPACE)) { + data <<= 1; + } + else { + return ERR; + } + offset++; + } + // Success + results->bits = SAMSUNG_BITS; + results->value = data; + results->decode_type = SAMSUNG; + return DECODED; +} + +/* ----------------------------------------------------------------------- + * hashdecode - decode an arbitrary IR code. + * Instead of decoding using a standard encoding scheme + * (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. + * + * The algorithm: look at the sequence of MARK signals, and see if each one + * is shorter (0), the same length (1), or longer (2) than the previous. + * Do the same with the SPACE signals. Hszh the resulting sequence of 0's, + * 1's, and 2's to a 32-bit value. This will give a unique value for each + * different code (probably), for most code systems. + * + * http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html + */ + +// Compare two tick values, returning 0 if newval is shorter, +// 1 if newval is equal, and 2 if newval is longer +// Use a tolerance of 20% +int IRrecv::compare(unsigned int oldval, unsigned int newval) { + if (newval < oldval * .8) { + return 0; + } + else if (oldval < newval * .8) { + return 2; + } + else { + return 1; + } +} + +// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param +#define FNV_PRIME_32 16777619 +#define FNV_BASIS_32 2166136261 + +/* Converts the raw code values into a 32-bit hash code. + * Hopefully this code is unique for each button. + * This isn't a "real" decoding, just an arbitrary value. + */ +long IRrecv::decodeHash(decode_results *results) { + // Require at least 6 samples to prevent triggering on noise + if (results->rawlen < 6) { + return ERR; + } + long hash = FNV_BASIS_32; + for (int i = 1; i+2 < results->rawlen; i++) { + int value = compare(results->rawbuf[i], results->rawbuf[i+2]); + // Add value into the hash + hash = (hash * FNV_PRIME_32) ^ value; + } + results->value = hash; + results->bits = 32; + results->decode_type = UNKNOWN; + return DECODED; +} + +/* Sharp and DISH support by Todd Treece ( http://unionbridge.org/design/ircommand ) + +The Dish send function needs to be repeated 4 times, and the Sharp function +has the necessary repeat built in because of the need to invert the signal. + +Sharp protocol documentation: +http://www.sbprojects.com/knowledge/ir/sharp.htm + +Here are the LIRC files that I found that seem to match the remote codes +from the oscilloscope: + +Sharp LCD TV: +http://lirc.sourceforge.net/remotes/sharp/GA538WJSA + +DISH NETWORK (echostar 301): +http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx + +For the DISH codes, only send the last for characters of the hex. +i.e. use 0x1C10 instead of 0x0000000000001C10 which is listed in the +linked LIRC file. +*/ + +void IRsend::sendSharpRaw(unsigned long data, int nbits) { + enableIROut(38); + + // Sending codes in bursts of 3 (normal, inverted, normal) makes transmission + // much more reliable. That's the exact behaviour of CD-S6470 remote control. + for (int n = 0; n < 3; n++) { + for (int i = 1 << (nbits-1); i > 0; i>>=1) { + if (data & i) { + mark(SHARP_BIT_MARK); + space(SHARP_ONE_SPACE); + } + else { + mark(SHARP_BIT_MARK); + space(SHARP_ZERO_SPACE); + } + } + + mark(SHARP_BIT_MARK); + space(SHARP_ZERO_SPACE); + delay(40); + + data = data ^ SHARP_TOGGLE_MASK; + } +} + +// Sharp send compatible with data obtained through decodeSharp +void IRsend::sendSharp(unsigned int address, unsigned int command) { + sendSharpRaw((address << 10) | (command << 2) | 2, 15); +} + +void IRsend::sendDISH(unsigned long data, int nbits) { + enableIROut(56); + mark(DISH_HDR_MARK); + space(DISH_HDR_SPACE); + for (int i = 0; i < nbits; i++) { + if (data & DISH_TOP_BIT) { + mark(DISH_BIT_MARK); + space(DISH_ONE_SPACE); + } + else { + mark(DISH_BIT_MARK); + space(DISH_ZERO_SPACE); + } + data <<= 1; + } +} diff --git a/Venus_Skeleton/libs/IRremote/IRremote.h b/Venus_Skeleton/libs/IRremote/IRremote.h new file mode 100644 index 0000000..e4b274e --- /dev/null +++ b/Venus_Skeleton/libs/IRremote/IRremote.h @@ -0,0 +1,130 @@ +/* + * IRremote + * Version 0.1 July, 2009 + * Copyright 2009 Ken Shirriff + * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.htm http://arcfn.com + * Edited by Mitra to add new controller SANYO + * + * Interrupt code based on NECIRrcv by Joe Knapp + * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 + * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ + * + * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) + * LG added by Darryl Smith (based on the JVC protocol) + * Whynter A/C ARC-110WD added by Francesco Meschia + */ + +#ifndef IRremote_h +#define IRremote_h + +// The following are compile-time library options. +// If you change them, recompile the library. +// If DEBUG is defined, a lot of debugging output will be printed during decoding. +// TEST must be defined for the IRtest unittests to work. It will make some +// methods virtual, which will be slightly slower, which is why it is optional. +//#define DEBUG +// #define TEST + +// Results returned from the decoder +class decode_results { +public: + int decode_type; // NEC, SONY, RC5, UNKNOWN + union { // This is used for decoding Panasonic and Sharp data + unsigned int panasonicAddress; + unsigned int sharpAddress; + }; + unsigned long value; // Decoded value + int bits; // Number of bits in decoded value + volatile unsigned int *rawbuf; // Raw intervals in .5 us ticks + int rawlen; // Number of records in rawbuf. +}; + +// Values for decode_type +#define NEC 1 +#define SONY 2 +#define RC5 3 +#define RC6 4 +#define DISH 5 +#define SHARP 6 +#define PANASONIC 7 +#define JVC 8 +#define SANYO 9 +#define MITSUBISHI 10 +#define SAMSUNG 11 +#define LG 12 +#define WHYNTER 13 +#define UNKNOWN -1 + +// Decoded value for NEC when a repeat code is received +#define REPEAT 0xffffffff + +// main class for receiving IR +class IRrecv +{ +public: + IRrecv(int recvpin); + void blink13(int blinkflag); + int decode(decode_results *results); + void enableIRIn(); + void resume(); +private: + // These are called by decode + int getRClevel(decode_results *results, int *offset, int *used, int t1); + long decodeNEC(decode_results *results); + long decodeSony(decode_results *results); + long decodeSanyo(decode_results *results); + long decodeMitsubishi(decode_results *results); + long decodeRC5(decode_results *results); + long decodeRC6(decode_results *results); + long decodePanasonic(decode_results *results); + long decodeLG(decode_results *results); + long decodeJVC(decode_results *results); + long decodeSAMSUNG(decode_results *results); + long decodeWhynter(decode_results *results); + long decodeHash(decode_results *results); + int compare(unsigned int oldval, unsigned int newval); + +} ; + +// Only used for testing; can remove virtual for shorter code +#ifdef TEST +#define VIRTUAL virtual +#else +#define VIRTUAL +#endif + +class IRsend +{ +public: + IRsend() {} + void sendWhynter(unsigned long data, int nbits); + void sendNEC(unsigned long data, int nbits); + void sendSony(unsigned long data, int nbits); + // Neither Sanyo nor Mitsubishi send is implemented yet + // void sendSanyo(unsigned long data, int nbits); + // void sendMitsubishi(unsigned long data, int nbits); + void sendRaw(unsigned int buf[], int len, int hz); + void sendRC5(unsigned long data, int nbits); + void sendRC6(unsigned long data, int nbits); + void sendDISH(unsigned long data, int nbits); + void sendSharp(unsigned int address, unsigned int command); + void sendSharpRaw(unsigned long data, int nbits); + void sendPanasonic(unsigned int address, unsigned long data); + void sendJVC(unsigned long data, int nbits, int repeat); // *Note instead of sending the REPEAT constant if you want the JVC repeat signal sent, send the original code value and change the repeat argument from 0 to 1. JVC protocol repeats by skipping the header NOT by sending a separate code value like NEC does. + // private: + void sendSAMSUNG(unsigned long data, int nbits); + void enableIROut(int khz); + VIRTUAL void mark(int usec); + VIRTUAL void space(int usec); +} ; + +// Some useful constants + +#define USECPERTICK 50 // microseconds per clock interrupt tick +#define RAWBUF 100 // Length of raw duration buffer + +// Marks tend to be 100us too long, and spaces 100us too short +// when received due to sensor lag. +#define MARK_EXCESS 100 + +#endif diff --git a/Venus_Skeleton/libs/IRremote/IRremoteInt.h b/Venus_Skeleton/libs/IRremote/IRremoteInt.h new file mode 100644 index 0000000..53167c2 --- /dev/null +++ b/Venus_Skeleton/libs/IRremote/IRremoteInt.h @@ -0,0 +1,525 @@ +/* + * IRremote + * Version 0.1 July, 2009 + * Copyright 2009 Ken Shirriff + * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html + * + * Modified by Paul Stoffregen to support other boards and timers + * + * Interrupt code based on NECIRrcv by Joe Knapp + * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 + * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ + * + * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) + * Whynter A/C ARC-110WD added by Francesco Meschia + */ + +#ifndef IRremoteint_h +#define IRremoteint_h + +#if defined(ARDUINO) && ARDUINO >= 100 +#include +#else +#include +#endif + +// define which timer to use +// +// Uncomment the timer you wish to use on your board. If you +// are using another library which uses timer2, you have options +// to switch IRremote to use a different timer. + +// Arduino Mega +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + //#define IR_USE_TIMER1 // tx = pin 11 + #define IR_USE_TIMER2 // tx = pin 9 + //#define IR_USE_TIMER3 // tx = pin 5 + //#define IR_USE_TIMER4 // tx = pin 6 + //#define IR_USE_TIMER5 // tx = pin 46 + +// Teensy 1.0 +#elif defined(__AVR_AT90USB162__) + #define IR_USE_TIMER1 // tx = pin 17 + +// Teensy 2.0 +#elif defined(__AVR_ATmega32U4__) + //#define IR_USE_TIMER1 // tx = pin 14 + //#define IR_USE_TIMER3 // tx = pin 9 + #define IR_USE_TIMER4_HS // tx = pin 10 + +// Teensy 3.0 +#elif defined(__MK20DX128__) + #define IR_USE_TIMER_CMT // tx = pin 5 + +// Teensy++ 1.0 & 2.0 +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) + //#define IR_USE_TIMER1 // tx = pin 25 + #define IR_USE_TIMER2 // tx = pin 1 + //#define IR_USE_TIMER3 // tx = pin 16 + +// Sanguino +#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) + //#define IR_USE_TIMER1 // tx = pin 13 + #define IR_USE_TIMER2 // tx = pin 14 + +// Atmega8 +#elif defined(__AVR_ATmega8P__) || defined(__AVR_ATmega8__) + #define IR_USE_TIMER1 // tx = pin 9 + +// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, etc +#else + //#define IR_USE_TIMER1 // tx = pin 9 + #define IR_USE_TIMER2 // tx = pin 3 +#endif + + + +#ifdef F_CPU +#define SYSCLOCK F_CPU // main Arduino clock +#else +#define SYSCLOCK 16000000 // main Arduino clock +#endif + +#define ERR 0 +#define DECODED 1 + + +// defines for setting and clearing register bits +#ifndef cbi +#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) +#endif +#ifndef sbi +#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) +#endif + +// Pulse parms are *50-100 for the Mark and *50+100 for the space +// First MARK is the one after the long gap +// pulse parameters in usec +#define WHYNTER_HDR_MARK 2850 +#define WHYNTER_HDR_SPACE 2850 +#define WHYNTER_BIT_MARK 750 +#define WHYNTER_ONE_MARK 750 +#define WHYNTER_ONE_SPACE 2150 +#define WHYNTER_ZERO_MARK 750 +#define WHYNTER_ZERO_SPACE 750 + +#define NEC_HDR_MARK 9000 +#define NEC_HDR_SPACE 4500 +#define NEC_BIT_MARK 560 +#define NEC_ONE_SPACE 1690 +#define NEC_ZERO_SPACE 560 +#define NEC_RPT_SPACE 2250 + +#define SONY_HDR_MARK 2400 +#define SONY_HDR_SPACE 600 +#define SONY_ONE_MARK 1200 +#define SONY_ZERO_MARK 600 +#define SONY_RPT_LENGTH 45000 +#define SONY_DOUBLE_SPACE_USECS 500 // usually ssee 713 - not using ticks as get number wrapround + +// SA 8650B +#define SANYO_HDR_MARK 3500 // seen range 3500 +#define SANYO_HDR_SPACE 950 // seen 950 +#define SANYO_ONE_MARK 2400 // seen 2400 +#define SANYO_ZERO_MARK 700 // seen 700 +#define SANYO_DOUBLE_SPACE_USECS 800 // usually ssee 713 - not using ticks as get number wrapround +#define SANYO_RPT_LENGTH 45000 + +// Mitsubishi RM 75501 +// 14200 7 41 7 42 7 42 7 17 7 17 7 18 7 41 7 18 7 17 7 17 7 18 7 41 8 17 7 17 7 18 7 17 7 + +// #define MITSUBISHI_HDR_MARK 250 // seen range 3500 +#define MITSUBISHI_HDR_SPACE 350 // 7*50+100 +#define MITSUBISHI_ONE_MARK 1950 // 41*50-100 +#define MITSUBISHI_ZERO_MARK 750 // 17*50-100 +// #define MITSUBISHI_DOUBLE_SPACE_USECS 800 // usually ssee 713 - not using ticks as get number wrapround +// #define MITSUBISHI_RPT_LENGTH 45000 + + +#define RC5_T1 889 +#define RC5_RPT_LENGTH 46000 + +#define RC6_HDR_MARK 2666 +#define RC6_HDR_SPACE 889 +#define RC6_T1 444 +#define RC6_RPT_LENGTH 46000 + +#define SHARP_BIT_MARK 245 +#define SHARP_ONE_SPACE 1805 +#define SHARP_ZERO_SPACE 795 +#define SHARP_GAP 600000 +#define SHARP_TOGGLE_MASK 0x3FF +#define SHARP_RPT_SPACE 3000 + +#define DISH_HDR_MARK 400 +#define DISH_HDR_SPACE 6100 +#define DISH_BIT_MARK 400 +#define DISH_ONE_SPACE 1700 +#define DISH_ZERO_SPACE 2800 +#define DISH_RPT_SPACE 6200 +#define DISH_TOP_BIT 0x8000 + +#define PANASONIC_HDR_MARK 3502 +#define PANASONIC_HDR_SPACE 1750 +#define PANASONIC_BIT_MARK 502 +#define PANASONIC_ONE_SPACE 1244 +#define PANASONIC_ZERO_SPACE 400 + +#define JVC_HDR_MARK 8000 +#define JVC_HDR_SPACE 4000 +#define JVC_BIT_MARK 600 +#define JVC_ONE_SPACE 1600 +#define JVC_ZERO_SPACE 550 +#define JVC_RPT_LENGTH 60000 + +#define LG_HDR_MARK 8000 +#define LG_HDR_SPACE 4000 +#define LG_BIT_MARK 600 +#define LG_ONE_SPACE 1600 +#define LG_ZERO_SPACE 550 +#define LG_RPT_LENGTH 60000 + +#define SAMSUNG_HDR_MARK 5000 +#define SAMSUNG_HDR_SPACE 5000 +#define SAMSUNG_BIT_MARK 560 +#define SAMSUNG_ONE_SPACE 1600 +#define SAMSUNG_ZERO_SPACE 560 +#define SAMSUNG_RPT_SPACE 2250 + + +#define SHARP_BITS 15 +#define DISH_BITS 16 + +#define TOLERANCE 25 // percent tolerance in measurements +#define LTOL (1.0 - TOLERANCE/100.) +#define UTOL (1.0 + TOLERANCE/100.) + +#define _GAP 5000 // Minimum map between transmissions +#define GAP_TICKS (_GAP/USECPERTICK) + +#define TICKS_LOW(us) (int) (((us)*LTOL/USECPERTICK)) +#define TICKS_HIGH(us) (int) (((us)*UTOL/USECPERTICK + 1)) + +// receiver states +#define STATE_IDLE 2 +#define STATE_MARK 3 +#define STATE_SPACE 4 +#define STATE_STOP 5 + +// information for the interrupt handler +typedef struct { + uint8_t recvpin; // pin for IR data from detector + uint8_t rcvstate; // state machine + uint8_t blinkflag; // TRUE to enable blinking of pin 13 on IR processing + unsigned int timer; // state timer, counts 50uS ticks. + unsigned int rawbuf[RAWBUF]; // raw data + uint8_t rawlen; // counter of entries in rawbuf +} +irparams_t; + +// Defined in IRremote.cpp +extern volatile irparams_t irparams; + +// IR detector output is active low +#define MARK 0 +#define SPACE 1 + +#define TOPBIT 0x80000000 + +#define NEC_BITS 32 +#define SONY_BITS 12 +#define SANYO_BITS 12 +#define MITSUBISHI_BITS 16 +#define MIN_RC5_SAMPLES 11 +#define MIN_RC6_SAMPLES 1 +#define PANASONIC_BITS 48 +#define JVC_BITS 16 +#define LG_BITS 28 +#define SAMSUNG_BITS 32 +#define WHYNTER_BITS 32 + + + + +// defines for timer2 (8 bits) +#if defined(IR_USE_TIMER2) +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR2A |= _BV(COM2B1)) +#define TIMER_DISABLE_PWM (TCCR2A &= ~(_BV(COM2B1))) +#define TIMER_ENABLE_INTR (TIMSK2 = _BV(OCIE2A)) +#define TIMER_DISABLE_INTR (TIMSK2 = 0) +#define TIMER_INTR_NAME TIMER2_COMPA_vect +#define TIMER_CONFIG_KHZ(val) ({ \ + const uint8_t pwmval = SYSCLOCK / 2000 / (val); \ + TCCR2A = _BV(WGM20); \ + TCCR2B = _BV(WGM22) | _BV(CS20); \ + OCR2A = pwmval; \ + OCR2B = pwmval / 3; \ +}) +#define TIMER_COUNT_TOP (SYSCLOCK * USECPERTICK / 1000000) +#if (TIMER_COUNT_TOP < 256) +#define TIMER_CONFIG_NORMAL() ({ \ + TCCR2A = _BV(WGM21); \ + TCCR2B = _BV(CS20); \ + OCR2A = TIMER_COUNT_TOP; \ + TCNT2 = 0; \ +}) +#else +#define TIMER_CONFIG_NORMAL() ({ \ + TCCR2A = _BV(WGM21); \ + TCCR2B = _BV(CS21); \ + OCR2A = TIMER_COUNT_TOP / 8; \ + TCNT2 = 0; \ +}) +#endif +#if defined(CORE_OC2B_PIN) +#define TIMER_PWM_PIN CORE_OC2B_PIN /* Teensy */ +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define TIMER_PWM_PIN 9 /* Arduino Mega */ +#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) +#define TIMER_PWM_PIN 14 /* Sanguino */ +#else +#define TIMER_PWM_PIN 3 /* Arduino Duemilanove, Diecimila, LilyPad, etc */ +#endif + + +// defines for timer1 (16 bits) +#elif defined(IR_USE_TIMER1) +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR1A |= _BV(COM1A1)) +#define TIMER_DISABLE_PWM (TCCR1A &= ~(_BV(COM1A1))) +#if defined(__AVR_ATmega8P__) || defined(__AVR_ATmega8__) + #define TIMER_ENABLE_INTR (TIMSK = _BV(OCIE1A)) + #define TIMER_DISABLE_INTR (TIMSK = 0) +#else + #define TIMER_ENABLE_INTR (TIMSK1 = _BV(OCIE1A)) + #define TIMER_DISABLE_INTR (TIMSK1 = 0) +#endif +#define TIMER_INTR_NAME TIMER1_COMPA_vect +#define TIMER_CONFIG_KHZ(val) ({ \ + const uint16_t pwmval = SYSCLOCK / 2000 / (val); \ + TCCR1A = _BV(WGM11); \ + TCCR1B = _BV(WGM13) | _BV(CS10); \ + ICR1 = pwmval; \ + OCR1A = pwmval / 3; \ +}) +#define TIMER_CONFIG_NORMAL() ({ \ + TCCR1A = 0; \ + TCCR1B = _BV(WGM12) | _BV(CS10); \ + OCR1A = SYSCLOCK * USECPERTICK / 1000000; \ + TCNT1 = 0; \ +}) +#if defined(CORE_OC1A_PIN) +#define TIMER_PWM_PIN CORE_OC1A_PIN /* Teensy */ +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define TIMER_PWM_PIN 11 /* Arduino Mega */ +#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) +#define TIMER_PWM_PIN 13 /* Sanguino */ +#else +#define TIMER_PWM_PIN 9 /* Arduino Duemilanove, Diecimila, LilyPad, etc */ +#endif + + +// defines for timer3 (16 bits) +#elif defined(IR_USE_TIMER3) +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR3A |= _BV(COM3A1)) +#define TIMER_DISABLE_PWM (TCCR3A &= ~(_BV(COM3A1))) +#define TIMER_ENABLE_INTR (TIMSK3 = _BV(OCIE3A)) +#define TIMER_DISABLE_INTR (TIMSK3 = 0) +#define TIMER_INTR_NAME TIMER3_COMPA_vect +#define TIMER_CONFIG_KHZ(val) ({ \ + const uint16_t pwmval = SYSCLOCK / 2000 / (val); \ + TCCR3A = _BV(WGM31); \ + TCCR3B = _BV(WGM33) | _BV(CS30); \ + ICR3 = pwmval; \ + OCR3A = pwmval / 3; \ +}) +#define TIMER_CONFIG_NORMAL() ({ \ + TCCR3A = 0; \ + TCCR3B = _BV(WGM32) | _BV(CS30); \ + OCR3A = SYSCLOCK * USECPERTICK / 1000000; \ + TCNT3 = 0; \ +}) +#if defined(CORE_OC3A_PIN) +#define TIMER_PWM_PIN CORE_OC3A_PIN /* Teensy */ +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define TIMER_PWM_PIN 5 /* Arduino Mega */ +#else +#error "Please add OC3A pin number here\n" +#endif + + +// defines for timer4 (10 bits, high speed option) +#elif defined(IR_USE_TIMER4_HS) +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR4A |= _BV(COM4A1)) +#define TIMER_DISABLE_PWM (TCCR4A &= ~(_BV(COM4A1))) +#define TIMER_ENABLE_INTR (TIMSK4 = _BV(TOIE4)) +#define TIMER_DISABLE_INTR (TIMSK4 = 0) +#define TIMER_INTR_NAME TIMER4_OVF_vect +#define TIMER_CONFIG_KHZ(val) ({ \ + const uint16_t pwmval = SYSCLOCK / 2000 / (val); \ + TCCR4A = (1<> 8; \ + OCR4C = pwmval; \ + TC4H = (pwmval / 3) >> 8; \ + OCR4A = (pwmval / 3) & 255; \ +}) +#define TIMER_CONFIG_NORMAL() ({ \ + TCCR4A = 0; \ + TCCR4B = _BV(CS40); \ + TCCR4C = 0; \ + TCCR4D = 0; \ + TCCR4E = 0; \ + TC4H = (SYSCLOCK * USECPERTICK / 1000000) >> 8; \ + OCR4C = (SYSCLOCK * USECPERTICK / 1000000) & 255; \ + TC4H = 0; \ + TCNT4 = 0; \ +}) +#if defined(CORE_OC4A_PIN) +#define TIMER_PWM_PIN CORE_OC4A_PIN /* Teensy */ +#elif defined(__AVR_ATmega32U4__) +#define TIMER_PWM_PIN 13 /* Leonardo */ +#else +#error "Please add OC4A pin number here\n" +#endif + + +// defines for timer4 (16 bits) +#elif defined(IR_USE_TIMER4) +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR4A |= _BV(COM4A1)) +#define TIMER_DISABLE_PWM (TCCR4A &= ~(_BV(COM4A1))) +#define TIMER_ENABLE_INTR (TIMSK4 = _BV(OCIE4A)) +#define TIMER_DISABLE_INTR (TIMSK4 = 0) +#define TIMER_INTR_NAME TIMER4_COMPA_vect +#define TIMER_CONFIG_KHZ(val) ({ \ + const uint16_t pwmval = SYSCLOCK / 2000 / (val); \ + TCCR4A = _BV(WGM41); \ + TCCR4B = _BV(WGM43) | _BV(CS40); \ + ICR4 = pwmval; \ + OCR4A = pwmval / 3; \ +}) +#define TIMER_CONFIG_NORMAL() ({ \ + TCCR4A = 0; \ + TCCR4B = _BV(WGM42) | _BV(CS40); \ + OCR4A = SYSCLOCK * USECPERTICK / 1000000; \ + TCNT4 = 0; \ +}) +#if defined(CORE_OC4A_PIN) +#define TIMER_PWM_PIN CORE_OC4A_PIN +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define TIMER_PWM_PIN 6 /* Arduino Mega */ +#else +#error "Please add OC4A pin number here\n" +#endif + + +// defines for timer5 (16 bits) +#elif defined(IR_USE_TIMER5) +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR5A |= _BV(COM5A1)) +#define TIMER_DISABLE_PWM (TCCR5A &= ~(_BV(COM5A1))) +#define TIMER_ENABLE_INTR (TIMSK5 = _BV(OCIE5A)) +#define TIMER_DISABLE_INTR (TIMSK5 = 0) +#define TIMER_INTR_NAME TIMER5_COMPA_vect +#define TIMER_CONFIG_KHZ(val) ({ \ + const uint16_t pwmval = SYSCLOCK / 2000 / (val); \ + TCCR5A = _BV(WGM51); \ + TCCR5B = _BV(WGM53) | _BV(CS50); \ + ICR5 = pwmval; \ + OCR5A = pwmval / 3; \ +}) +#define TIMER_CONFIG_NORMAL() ({ \ + TCCR5A = 0; \ + TCCR5B = _BV(WGM52) | _BV(CS50); \ + OCR5A = SYSCLOCK * USECPERTICK / 1000000; \ + TCNT5 = 0; \ +}) +#if defined(CORE_OC5A_PIN) +#define TIMER_PWM_PIN CORE_OC5A_PIN +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define TIMER_PWM_PIN 46 /* Arduino Mega */ +#else +#error "Please add OC5A pin number here\n" +#endif + + +// defines for special carrier modulator timer +#elif defined(IR_USE_TIMER_CMT) +#define TIMER_RESET ({ \ + uint8_t tmp = CMT_MSC; \ + CMT_CMD2 = 30; \ +}) +#define TIMER_ENABLE_PWM CORE_PIN5_CONFIG = PORT_PCR_MUX(2)|PORT_PCR_DSE|PORT_PCR_SRE +#define TIMER_DISABLE_PWM CORE_PIN5_CONFIG = PORT_PCR_MUX(1)|PORT_PCR_DSE|PORT_PCR_SRE +#define TIMER_ENABLE_INTR NVIC_ENABLE_IRQ(IRQ_CMT) +#define TIMER_DISABLE_INTR NVIC_DISABLE_IRQ(IRQ_CMT) +#define TIMER_INTR_NAME cmt_isr +#ifdef ISR +#undef ISR +#endif +#define ISR(f) void f(void) +#if F_BUS == 48000000 +#define CMT_PPS_VAL 5 +#else +#define CMT_PPS_VAL 2 +#endif +#define TIMER_CONFIG_KHZ(val) ({ \ + SIM_SCGC4 |= SIM_SCGC4_CMT; \ + SIM_SOPT2 |= SIM_SOPT2_PTD7PAD; \ + CMT_PPS = CMT_PPS_VAL; \ + CMT_CGH1 = 2667 / val; \ + CMT_CGL1 = 5333 / val; \ + CMT_CMD1 = 0; \ + CMT_CMD2 = 30; \ + CMT_CMD3 = 0; \ + CMT_CMD4 = 0; \ + CMT_OC = 0x60; \ + CMT_MSC = 0x01; \ +}) +#define TIMER_CONFIG_NORMAL() ({ \ + SIM_SCGC4 |= SIM_SCGC4_CMT; \ + CMT_PPS = CMT_PPS_VAL; \ + CMT_CGH1 = 1; \ + CMT_CGL1 = 1; \ + CMT_CMD1 = 0; \ + CMT_CMD2 = 30; \ + CMT_CMD3 = 0; \ + CMT_CMD4 = 19; \ + CMT_OC = 0; \ + CMT_MSC = 0x03; \ +}) +#define TIMER_PWM_PIN 5 + + +#else // unknown timer +#error "Internal code configuration error, no known IR_USE_TIMER# defined\n" +#endif + + +// defines for blinking the LED +#if defined(CORE_LED0_PIN) +#define BLINKLED CORE_LED0_PIN +#define BLINKLED_ON() (digitalWrite(CORE_LED0_PIN, HIGH)) +#define BLINKLED_OFF() (digitalWrite(CORE_LED0_PIN, LOW)) +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define BLINKLED 13 +#define BLINKLED_ON() (PORTB |= B10000000) +#define BLINKLED_OFF() (PORTB &= B01111111) +#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) +#define BLINKLED 0 +#define BLINKLED_ON() (PORTD |= B00000001) +#define BLINKLED_OFF() (PORTD &= B11111110) +#else +#define BLINKLED 13 +#define BLINKLED_ON() (PORTB |= B00100000) +#define BLINKLED_OFF() (PORTB &= B11011111) +#endif + +#endif -- cgit v1.2.1