└── BlynkIR ├── .gitignore ├── src ├── IRutils.h ├── IRtimer.h ├── IRtimer.cpp ├── WidgetMap.h ├── WidgetLCD.h ├── WidgetLED.h ├── IRutils.cpp ├── WidgetGPS.h ├── WidgetRTC.h ├── WidgetTable.h ├── WidgetTerminal.h ├── WidgetSD.h ├── WidgetBridge.h ├── WidgetTimeInput.h ├── IRsend.h └── IRsend.cpp └── BlynkIR.ino /BlynkIR/.gitignore: -------------------------------------------------------------------------------- 1 | auth.h 2 | -------------------------------------------------------------------------------- /BlynkIR/src/IRutils.h: -------------------------------------------------------------------------------- 1 | #ifndef IRUTILS_H_ 2 | #define IRUTILS_H_ 3 | 4 | // Copyright 2017 David Conran 5 | 6 | #define __STDC_LIMIT_MACROS 7 | #include 8 | 9 | uint64_t reverseBits(uint64_t input, uint16_t nbits); 10 | 11 | #endif // IRUTILS_H_ 12 | -------------------------------------------------------------------------------- /BlynkIR/src/IRtimer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 David Conran 2 | 3 | #ifndef IRTIMER_H_ 4 | #define IRTIMER_H_ 5 | 6 | #define __STDC_LIMIT_MACROS 7 | #include 8 | 9 | // Classes 10 | class IRtimer { 11 | public: 12 | IRtimer(); 13 | void reset(); 14 | uint32_t elapsed(); 15 | 16 | private: 17 | uint32_t start; 18 | }; 19 | 20 | #endif // IRTIMER_H_ 21 | -------------------------------------------------------------------------------- /BlynkIR/src/IRtimer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 David Conran 2 | 3 | #include "IRtimer.h" 4 | #ifndef UNIT_TEST 5 | #include 6 | #endif 7 | 8 | // This class performs a simple time in useconds since instantiated. 9 | // Handles when the system timer wraps around (once). 10 | 11 | IRtimer::IRtimer() { 12 | reset(); 13 | } 14 | 15 | void IRtimer::reset() { 16 | #ifndef UNIT_TEST 17 | start = micros(); 18 | #else 19 | start = 0; 20 | #endif 21 | } 22 | 23 | uint32_t IRtimer::elapsed() { 24 | #ifndef UNIT_TEST 25 | uint32_t now = micros(); 26 | #else 27 | uint32_t now = 0; 28 | #endif 29 | if (start <= now) // Check if the system timer has wrapped. 30 | return now - start; // No wrap. 31 | else 32 | return UINT32_MAX - start + now; // Has wrapped. 33 | } 34 | -------------------------------------------------------------------------------- /BlynkIR/src/WidgetMap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WidgetMap.h 3 | * @author Volodymyr Shymanskyy 4 | * @license This project is released under the MIT License (MIT) 5 | * @copyright Copyright (c) 2016 Volodymyr Shymanskyy 6 | * @date Nov 2016 7 | * @brief 8 | */ 9 | 10 | #ifndef WidgetMap_h 11 | #define WidgetMap_h 12 | 13 | #include 14 | 15 | class WidgetMap 16 | : public BlynkWidgetBase 17 | { 18 | public: 19 | WidgetMap(uint8_t vPin) : BlynkWidgetBase(vPin) {} 20 | 21 | void clear() { 22 | Blynk.virtualWrite(mPin, "clr"); 23 | } 24 | 25 | template 26 | void location(const T1& index, const T2& lat, const T3& lon, const T4& value) { 27 | Blynk.virtualWrite(mPin, index, lat, lon, value); 28 | } 29 | 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /BlynkIR/src/WidgetLCD.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WidgetLCD.h 3 | * @author Volodymyr Shymanskyy 4 | * @license This project is released under the MIT License (MIT) 5 | * @copyright Copyright (c) 2015 Volodymyr Shymanskyy 6 | * @date Mar 2015 7 | * @brief 8 | */ 9 | 10 | #ifndef WidgetLCD_h 11 | #define WidgetLCD_h 12 | 13 | #include 14 | 15 | class WidgetLCD 16 | : public BlynkWidgetBase 17 | { 18 | public: 19 | WidgetLCD(uint8_t vPin) : BlynkWidgetBase(vPin) {} 20 | 21 | void clear() { 22 | Blynk.virtualWrite(mPin, "clr"); 23 | } 24 | 25 | template 26 | void print(int x, int y, const T& str) { 27 | char mem[BLYNK_MAX_SENDBYTES]; 28 | BlynkParam cmd(mem, 0, sizeof(mem)); 29 | cmd.add("p"); 30 | cmd.add(x); 31 | cmd.add(y); 32 | cmd.add(str); 33 | Blynk.virtualWrite(mPin, cmd); 34 | } 35 | 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /BlynkIR/src/WidgetLED.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WidgetLED.h 3 | * @author Volodymyr Shymanskyy 4 | * @license This project is released under the MIT License (MIT) 5 | * @copyright Copyright (c) 2015 Volodymyr Shymanskyy 6 | * @date Apr 2015 7 | * @brief 8 | */ 9 | 10 | #ifndef WidgetLED_h 11 | #define WidgetLED_h 12 | 13 | #include 14 | 15 | class WidgetLED 16 | : public BlynkWidgetBase 17 | { 18 | public: 19 | WidgetLED(uint8_t vPin) 20 | : BlynkWidgetBase(vPin) 21 | , mValue(0) 22 | {} 23 | 24 | uint8_t getValue() const { 25 | return mValue; 26 | } 27 | 28 | void setValue(uint8_t value) { 29 | mValue = value; 30 | Blynk.virtualWrite(mPin, value); 31 | } 32 | 33 | void on() { 34 | setValue(255); 35 | } 36 | 37 | void off() { 38 | setValue(0); 39 | } 40 | 41 | private: 42 | uint8_t mValue; 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /BlynkIR/src/IRutils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 David Conran 2 | 3 | #include "IRutils.h" 4 | #ifndef UNIT_TEST 5 | #include 6 | #endif 7 | #define __STDC_LIMIT_MACROS 8 | #include 9 | #include 10 | 11 | // Reverse the order of the requested least significant nr. of bits. 12 | // Args: 13 | // input: Bit pattern/integer to reverse. 14 | // nbits: Nr. of bits to reverse. 15 | // Returns: 16 | // The reversed bit pattern. 17 | uint64_t reverseBits(uint64_t input, uint16_t nbits) { 18 | if (nbits <= 1) 19 | return input; // Reversing <= 1 bits makes no change at all. 20 | // Cap the nr. of bits to rotate to the max nr. of bits in the input. 21 | nbits = std::min(nbits, (uint16_t) (sizeof(input) * 8)); 22 | uint64_t output = 0; 23 | for (uint16_t i = 0; i < nbits; i++) { 24 | output <<= 1; 25 | output |= (input & 1); 26 | input >>= 1; 27 | } 28 | // Merge any remaining unreversed bits back to the top of the reversed bits. 29 | return (input << nbits) | output; 30 | } 31 | -------------------------------------------------------------------------------- /BlynkIR/src/WidgetGPS.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WidgetGPS.h 3 | * @author Volodymyr Shymanskyy 4 | * @license This project is released under the MIT License (MIT) 5 | * @copyright Copyright (c) 2015 Volodymyr Shymanskyy 6 | * @date Oct 2016 7 | * @brief 8 | * 9 | */ 10 | 11 | #ifndef WidgetGPS_h 12 | #define WidgetGPS_h 13 | 14 | #ifndef BLYNK_NO_FLOAT 15 | 16 | #include 17 | 18 | class GpsParam 19 | { 20 | public: 21 | 22 | GpsParam(const BlynkParam& param) 23 | : mLat (0) 24 | , mLon (0) 25 | , mAlt (0) 26 | , mSpeed (0) 27 | { 28 | BlynkParam::iterator it = param.begin(); 29 | if (it >= param.end()) 30 | return; 31 | 32 | mLat = it.asDouble(); 33 | 34 | if (++it >= param.end()) 35 | return; 36 | 37 | mLon = it.asDouble(); 38 | 39 | if (++it >= param.end()) 40 | return; 41 | 42 | mAlt = it.asDouble(); 43 | 44 | if (++it >= param.end()) 45 | return; 46 | 47 | mSpeed = it.asDouble(); 48 | } 49 | 50 | 51 | double getLat() const { return mLat; } 52 | double getLon() const { return mLon; } 53 | double getAltitude() const { return mAlt; } 54 | double getSpeed() const { return mSpeed; } 55 | 56 | private: 57 | double mLat; 58 | double mLon; 59 | double mAlt; 60 | double mSpeed; 61 | }; 62 | 63 | #endif 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /BlynkIR/src/WidgetRTC.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WidgetRTC.h 3 | * @author Volodymyr Shymanskyy 4 | * @license This project is released under the MIT License (MIT) 5 | * @copyright Copyright (c) 2015 Volodymyr Shymanskyy 6 | * @date Mar 2016 7 | * @brief 8 | * 9 | */ 10 | 11 | #ifndef WidgetRTC_h 12 | #define WidgetRTC_h 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | class WidgetRTC 19 | : public BlynkSingleton 20 | { 21 | public: 22 | WidgetRTC() {} 23 | void begin(); 24 | 25 | private: 26 | static time_t requestTimeSync(); 27 | }; 28 | 29 | // This is called by Time library when it needs time sync 30 | time_t WidgetRTC::requestTimeSync() 31 | { 32 | // Request RTC widget update from the server 33 | Blynk.sendInternal("rtc", "sync"); 34 | // Tell the Time library that we'll set it later 35 | return 0; 36 | } 37 | 38 | inline 39 | void WidgetRTC::begin() 40 | { 41 | setSyncProvider(requestTimeSync); 42 | } 43 | 44 | BLYNK_WRITE(InternalPinRTC) { 45 | const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 46 | unsigned long blynkTime = param.asLong(); 47 | 48 | if (blynkTime >= DEFAULT_TIME) { // Check the integer is a valid time (greater than Jan 1 2013) 49 | setTime(blynkTime); // Sync Time library clock to the value received from Blynk 50 | BLYNK_LOG1(BLYNK_F("Time sync: OK")); 51 | } 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /BlynkIR/src/WidgetTable.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WidgetTable.h 3 | * @author Volodymyr Shymanskyy 4 | * @license This project is released under the MIT License (MIT) 5 | * @copyright Copyright (c) 2015 Volodymyr Shymanskyy 6 | * @date Sep 2016 7 | * @brief 8 | * 9 | */ 10 | 11 | #ifndef WidgetTable_h 12 | #define WidgetTable_h 13 | 14 | #include 15 | 16 | class WidgetTable 17 | : public BlynkWidgetBase 18 | { 19 | public: 20 | typedef void (*ItemSelectChange)(int index, bool selected); 21 | typedef void (*ItemOrderChange)(int indexFrom, int indexTo); 22 | 23 | public: 24 | WidgetTable(uint8_t vPin = -1) 25 | : BlynkWidgetBase(vPin) 26 | , mOnOrderChange(NULL) 27 | , mOnSelectChange(NULL) 28 | {} 29 | 30 | void onWrite(BlynkReq BLYNK_UNUSED &request, const BlynkParam& param) { 31 | if (mOnOrderChange && 0 == strcmp(param[0].asStr(), "order")) { 32 | mOnOrderChange(param[1].asInt(), param[2].asInt()); 33 | } else if (mOnSelectChange && 0 == strcmp(param[0].asStr(), "select")) { 34 | mOnSelectChange(param[1].asInt(), true); 35 | } else if (mOnSelectChange && 0 == strcmp(param[0].asStr(), "deselect")) { 36 | mOnSelectChange(param[1].asInt(), false); 37 | } 38 | } 39 | 40 | void onOrderChange(ItemOrderChange cbk) { mOnOrderChange = cbk; } 41 | void onSelectChange(ItemSelectChange cbk) { mOnSelectChange = cbk; } 42 | 43 | void clear() { 44 | Blynk.virtualWrite(mPin, "clr"); 45 | } 46 | 47 | template 48 | void addRow(int index, const T1& name, const T2& value) { 49 | Blynk.virtualWrite(mPin, "add", index, name, value); 50 | } 51 | 52 | void pickRow(int index) { 53 | Blynk.virtualWrite(mPin, "pick", index); 54 | } 55 | 56 | private: 57 | ItemOrderChange mOnOrderChange; 58 | ItemSelectChange mOnSelectChange; 59 | 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /BlynkIR/src/WidgetTerminal.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WidgetTerminal.h 3 | * @author Volodymyr Shymanskyy 4 | * @license This project is released under the MIT License (MIT) 5 | * @copyright Copyright (c) 2015 Volodymyr Shymanskyy 6 | * @date Jan 2015 7 | * @brief 8 | */ 9 | 10 | #ifndef WidgetTerminal_h 11 | #define WidgetTerminal_h 12 | 13 | #if !(defined(LINUX) || defined(MBED_LIBRARY_VERSION)) 14 | #define BLYNK_USE_PRINT_CLASS 15 | #endif 16 | 17 | #include 18 | 19 | #ifdef BLYNK_USE_PRINT_CLASS 20 | #if !(defined(SPARK) || defined(PARTICLE) || (PLATFORM_ID==88) || defined(ARDUINO_RedBear_Duo)) // 88 -> RBL Duo 21 | // On Particle this is auto-included 22 | #include 23 | #endif 24 | #endif 25 | 26 | class WidgetTerminal 27 | : public BlynkWidgetBase 28 | #ifdef BLYNK_USE_PRINT_CLASS 29 | , public Print 30 | #endif 31 | { 32 | public: 33 | WidgetTerminal(uint8_t vPin) 34 | : BlynkWidgetBase(vPin) 35 | , mOutQty(0) 36 | {} 37 | 38 | //virtual ~WidgetTerminal() {} 39 | 40 | virtual size_t write(uint8_t byte) { 41 | mOutBuf[mOutQty++] = byte; 42 | if (mOutQty >= sizeof(mOutBuf)) { 43 | flush(); 44 | } 45 | return 1; 46 | } 47 | 48 | void flush() { 49 | if (mOutQty) { 50 | Blynk.virtualWriteBinary(mPin, mOutBuf, mOutQty); 51 | mOutQty = 0; 52 | } 53 | } 54 | 55 | #ifdef BLYNK_USE_PRINT_CLASS 56 | 57 | using Print::write; 58 | 59 | size_t write(const void* buff, size_t len) { 60 | return write((char*)buff, len); 61 | } 62 | 63 | #else 64 | 65 | size_t write(const void* buff, size_t len) { 66 | uint8_t* buf = (uint8_t*)buff; 67 | while (len--) { 68 | write(*buf++); 69 | } 70 | return len; 71 | } 72 | 73 | size_t write(const char* str) { 74 | return write(str, strlen(str)); 75 | } 76 | 77 | #endif 78 | 79 | private: 80 | uint8_t mOutBuf[64]; 81 | uint8_t mOutQty; 82 | }; 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /BlynkIR/src/WidgetSD.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WidgetSD.h 3 | * @author Volodymyr Shymanskyy 4 | * @license This project is released under the MIT License (MIT) 5 | * @copyright Copyright (c) 2015 Volodymyr Shymanskyy 6 | * @date Mar 2015 7 | * @brief 8 | * 9 | */ 10 | 11 | #ifndef WidgetSD_h 12 | #define WidgetSD_h 13 | 14 | #include 15 | 16 | class WidgetSD 17 | : public BlynkWidgetBase 18 | { 19 | public: 20 | WidgetSD(uint8_t vPin = -1) : BlynkWidgetBase(vPin) {} 21 | void onWrite(BlynkReq& request, const BlynkParam& param); 22 | }; 23 | 24 | #include 25 | void WidgetSD::onWrite(BlynkReq BLYNK_UNUSED &request, const BlynkParam& param) 26 | { 27 | const char* cmd = param[0].asStr(); 28 | if (!strcmp(cmd, "ls")) { 29 | if (File dir = SD.open(param[1].asStr())) { 30 | while (File entry = dir.openNextFile()) { 31 | char mem[32]; 32 | BlynkParam result(mem, 0, sizeof(mem)); 33 | result.add(entry.name()); 34 | if (entry.isDirectory()) { 35 | result.add("/"); 36 | } else { 37 | result.add(entry.size()); 38 | } 39 | Blynk.virtualWrite(request.pin, result); 40 | entry.close(); 41 | } 42 | dir.close(); 43 | } 44 | } else if (!strcmp(cmd, "get")) { // dc dc dc dc d[l|e] 45 | if (File f = SD.open(param[1].asStr())) { 46 | if (!f.isDirectory()) { 47 | char mem[32] = "dc"; 48 | const int maxlen = sizeof(mem)-3; 49 | int len; 50 | do { 51 | len = f.read(mem+3, maxlen); 52 | if (len < 0) { 53 | mem[1] = 'e'; // Error! 54 | len = 0; 55 | } else if (len < maxlen) { 56 | mem[1] = 'l'; // Last chunk 57 | } 58 | Blynk.virtualWriteBinary(request.pin, mem, len + 3); 59 | } while (len == maxlen); 60 | } else { 61 | 62 | } 63 | 64 | f.close(); 65 | } 66 | } else { 67 | //BLYNK_LOG2(BLYNK_F("Invalid SD command: "), cmd); 68 | } 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /BlynkIR/src/WidgetBridge.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WidgetBridge.h 3 | * @author Volodymyr Shymanskyy 4 | * @license This project is released under the MIT License (MIT) 5 | * @copyright Copyright (c) 2015 Volodymyr Shymanskyy 6 | * @date Apr 2015 7 | * @brief 8 | * 9 | */ 10 | 11 | #ifndef WidgetBridge_h 12 | #define WidgetBridge_h 13 | 14 | #include 15 | 16 | class WidgetBridge 17 | : private BlynkWidgetBase 18 | { 19 | public: 20 | WidgetBridge(uint8_t vPin) 21 | : BlynkWidgetBase(vPin) 22 | {} 23 | 24 | void setAuthToken(const char* token) { 25 | char mem[BLYNK_MAX_SENDBYTES]; 26 | BlynkParam cmd(mem, 0, sizeof(mem)); 27 | cmd.add(mPin); 28 | cmd.add("i"); 29 | cmd.add(token); 30 | Blynk.sendCmd(BLYNK_CMD_BRIDGE, 0, cmd.getBuffer(), cmd.getLength()-1); 31 | } 32 | 33 | template 34 | void digitalWrite(const T& pin, int val) { 35 | char mem[BLYNK_MAX_SENDBYTES]; 36 | BlynkParam cmd(mem, 0, sizeof(mem)); 37 | cmd.add(mPin); 38 | cmd.add("dw"); 39 | cmd.add(pin); 40 | cmd.add(val); 41 | Blynk.sendCmd(BLYNK_CMD_BRIDGE, 0, cmd.getBuffer(), cmd.getLength()-1); 42 | } 43 | 44 | template 45 | void analogWrite(const T& pin, int val) { 46 | char mem[BLYNK_MAX_SENDBYTES]; 47 | BlynkParam cmd(mem, 0, sizeof(mem)); 48 | cmd.add(mPin); 49 | cmd.add("aw"); 50 | cmd.add(pin); 51 | cmd.add(val); 52 | Blynk.sendCmd(BLYNK_CMD_BRIDGE, 0, cmd.getBuffer(), cmd.getLength()-1); 53 | } 54 | 55 | template 56 | void virtualWrite(int pin, Args... values) { 57 | char mem[BLYNK_MAX_SENDBYTES]; 58 | BlynkParam cmd(mem, 0, sizeof(mem)); 59 | cmd.add(mPin); 60 | cmd.add("vw"); 61 | cmd.add(pin); 62 | cmd.add_multi(values...); 63 | Blynk.sendCmd(BLYNK_CMD_BRIDGE, 0, cmd.getBuffer(), cmd.getLength()-1); 64 | } 65 | 66 | void virtualWriteBinary(int pin, const void* buff, size_t len) { 67 | char mem[8]; 68 | BlynkParam cmd(mem, 0, sizeof(mem)); 69 | cmd.add(mPin); 70 | cmd.add("vw"); 71 | cmd.add(pin); 72 | Blynk.sendCmd(BLYNK_CMD_BRIDGE, 0, cmd.getBuffer(), cmd.getLength(), buff, len); 73 | } 74 | 75 | void virtualWrite(int pin, const BlynkParam& param) { 76 | virtualWriteBinary(pin, param.getBuffer(), param.getLength()-1); 77 | } 78 | 79 | }; 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /BlynkIR/src/WidgetTimeInput.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WidgetTimeInput.h 3 | * @author Volodymyr Shymanskyy 4 | * @license This project is released under the MIT License (MIT) 5 | * @copyright Copyright (c) 2015 Volodymyr Shymanskyy 6 | * @date Aug 2016 7 | * @brief 8 | * 9 | */ 10 | 11 | #ifndef WidgetTimeInput_h 12 | #define WidgetTimeInput_h 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | class TimeInputParam 19 | { 20 | public: 21 | enum TimeMode { 22 | TIME_UNDEFINED, 23 | TIME_SUNSET, 24 | TIME_SUNRISE, 25 | TIME_SPECIFIED 26 | }; 27 | 28 | TimeInputParam(const BlynkParam& param) 29 | { 30 | mStartMode = TIME_UNDEFINED; 31 | mStopMode = TIME_UNDEFINED; 32 | mTZ[0] = '\0'; 33 | mWeekdays = -1; // All set 34 | mTZ_Offset = 0; 35 | 36 | BlynkParam::iterator it = param.begin(); 37 | if (it >= param.end()) 38 | return; 39 | 40 | if (0 == strcmp(it.asStr(), "sr")) { 41 | mStartMode = TIME_SUNRISE; 42 | } else if (0 == strcmp(it.asStr(), "ss")) { 43 | mStartMode = TIME_SUNSET; 44 | } else if (!it.isEmpty()) { 45 | mStart = BlynkTime(it.asLong()); 46 | if (mStart.isValid()) { 47 | mStartMode = TIME_SPECIFIED; 48 | } 49 | } 50 | 51 | if (++it >= param.end()) 52 | return; 53 | 54 | if (0 == strcmp(it.asStr(), "sr")) { 55 | mStopMode = TIME_SUNRISE; 56 | } else if (0 == strcmp(it.asStr(), "ss")) { 57 | mStopMode = TIME_SUNSET; 58 | } else if (!it.isEmpty()) { 59 | mStop = BlynkTime(it.asLong()); 60 | if (mStop.isValid()) { 61 | mStopMode = TIME_SPECIFIED; 62 | } 63 | } 64 | 65 | if (++it >= param.end()) 66 | return; 67 | 68 | strncpy(mTZ, it.asStr(), sizeof(mTZ)); 69 | 70 | if (++it >= param.end()) 71 | return; 72 | 73 | if (!it.isEmpty()) { 74 | mWeekdays = 0; 75 | const char* p = it.asStr(); 76 | 77 | while (int c = *p++) { 78 | if (c >= '1' && c <= '7') { 79 | BlynkBitSet(mWeekdays, c - '1'); 80 | } 81 | } 82 | } 83 | 84 | if (++it >= param.end()) 85 | return; 86 | 87 | mTZ_Offset = it.asLong(); 88 | } 89 | 90 | BlynkTime& getStart() { return mStart; } 91 | BlynkTime& getStop() { return mStop; } 92 | 93 | TimeMode getStartMode() const { return mStartMode; } 94 | TimeMode getStopMode() const { return mStopMode; } 95 | 96 | bool hasStartTime() const { return mStartMode == TIME_SPECIFIED; } 97 | bool isStartSunrise() const { return mStartMode == TIME_SUNRISE; } 98 | bool isStartSunset() const { return mStartMode == TIME_SUNSET; } 99 | int getStartHour() const { return mStart.hour(); } 100 | int getStartMinute() const { return mStart.minute(); } 101 | int getStartSecond() const { return mStart.second(); } 102 | 103 | bool hasStopTime() const { return mStopMode == TIME_SPECIFIED; } 104 | bool isStopSunrise() const { return mStopMode == TIME_SUNRISE; } 105 | bool isStopSunset() const { return mStopMode == TIME_SUNSET; } 106 | int getStopHour() const { return mStop.hour(); } 107 | int getStopMinute() const { return mStop.minute(); } 108 | int getStopSecond() const { return mStop.second(); } 109 | 110 | const char* getTZ() const { return mTZ; } 111 | int32_t getTZ_Offset() const { return mTZ_Offset; } 112 | 113 | bool isWeekdaySelected(int day) const { 114 | return BlynkBitRead(mWeekdays, (day - 1) % 7); 115 | } 116 | 117 | private: 118 | BlynkTime mStart; 119 | BlynkTime mStop; 120 | char mTZ[32]; 121 | int32_t mTZ_Offset; 122 | 123 | TimeMode mStopMode; 124 | TimeMode mStartMode; 125 | 126 | uint8_t mWeekdays; 127 | }; 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /BlynkIR/BlynkIR.ino: -------------------------------------------------------------------------------- 1 | /* IRremoteESP32: IRsendDemo - demonstrates sending IR codes with IRsend. 2 | 3 | Version 1.0 June 2017 4 | Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, 5 | Copyright 2009 Ken Shirriff, http://arcfn.com 6 | 7 | An IR LED with resistor and NPN transistor must be connected 8 | 9 | Suggested circuit: 10 | https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending 11 | 12 | Common mistakes & tips: 13 | * * Don't just connect the IR LED directly to the pin, it won't 14 | have enough current to drive the IR LED effectively. 15 | * * Make sure you have the IR LED polarity correct. 16 | See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity 17 | * * Typical digital camera/phones can be used to see if the IR LED is flashed. 18 | Replace the IR LED with a normal LED if you don't have a digital camera 19 | when debugging. 20 | * * Avoid using the following pins unless you really know what you are doing: 21 | * * Pin 0/D3: Can interfere with the boot/program mode & support circuits. 22 | * * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. 23 | * * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. 24 | 25 | */ 26 | //Includes 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "src/IRsend.h" 33 | #include "auth.h" //this file contains auth, ssid and pass 34 | //char auth[] = "1234567890" //Blynk Auth Token; 35 | //char ssid[] = "WiFi SSID"; 36 | //char pass[] = "password"; 37 | 38 | //IR protocol constants 39 | #define ONEMARK 400 //Nr. of usecs for the led to be pulsed for a '1' bit. 40 | #define ONESPACE 400 //Nr. of usecs for the led to be fully off for a '1' bit. 41 | #define ZEROMARK 400 //Nr. of usecs for the led to be pulsed for a '0' bit. 42 | #define ZEROSPACE 1200 //Nr. of usecs for the led to be fully off for a '0' bit. 43 | //#define IRFREQ 38000 //Carrier frequency 44 | int IRFREQ = 37000; 45 | #define IRDUTY 45 //Duty cycle % 46 | #define IRLED 26 //Output connected to IR LED 47 | #define BLYNK_PRINT Serial 48 | #define GPIO_LED GPIO_NUM_27 //status LED, GPIO 27 49 | #define RESET 1111 //start and stop time value when not active, 50 | //has to be different than 0 and not more than 24 51 | 52 | BlynkTimer timer; 53 | WidgetRTC rtc; 54 | WidgetLED led1(V2); //on/off indicator on Blynk app 55 | IRsend irsend(IRLED); //irsend object 56 | 57 | String startTime, stopTime; 58 | int currentHour = 0, currentMin = 0; 59 | int startHour = RESET, startMin = RESET; 60 | int stopHour = RESET, stopMin = RESET; 61 | boolean AC_ON = false; 62 | int Button = 0; //V4 63 | 64 | 65 | 66 | //Function that gets start time from Blynk app 67 | BLYNK_WRITE(V0) { 68 | TimeInputParam t(param); 69 | startHour = t.getStartHour(); 70 | startMin = t.getStartMinute(); 71 | startTime = String(t.getStartHour()) + ":" + String(t.getStartMinute()); 72 | Serial.println(String("Start time: ") + startTime); 73 | } 74 | 75 | //Function that gets stop time from Blynk app 76 | BLYNK_WRITE(V1) { 77 | TimeInputParam t(param); 78 | stopHour = t.getStartHour(); 79 | stopMin = t.getStartMinute(); 80 | stopTime = String(t.getStartHour()) + ":" + String(t.getStartMinute()); 81 | Serial.println(String("Stop time: ") + stopTime); 82 | } 83 | 84 | //Function that reads Start/stop button V4 85 | BLYNK_WRITE(V4) { 86 | Button = param.asInt(); 87 | } 88 | 89 | BLYNK_WRITE(V5) { 90 | IRFREQ = param.asInt()*10; 91 | } 92 | 93 | // Digital clock display of the time 94 | void clockDisplay() 95 | { 96 | // You can call hour(), minute(), ... at any time 97 | // Please see Time library examples for details 98 | 99 | String currentTime = String(hour()) + ":" + minute(); 100 | currentHour = hour(); 101 | currentMin = minute(); 102 | //String currentDate = String(day()) + " " + month() + " " + year(); 103 | Serial.print("Current time: "); 104 | Serial.println(currentTime); 105 | //Serial.print(currentDate); 106 | //Serial.println(); 107 | 108 | // Send time to the App 109 | Blynk.virtualWrite(V3, currentTime); 110 | // Send date to the App 111 | //Blynk.virtualWrite(V2, currentDate); 112 | 113 | } 114 | 115 | void sendIR() 116 | { 117 | irsend.enableIROut(IRFREQ, IRDUTY); //frequency, duty cycle 118 | //send header 119 | irsend.mark(3200); 120 | irsend.space(1600); 121 | //send command 122 | irsend.sendData(ONEMARK, ONESPACE, ZEROMARK, ZEROSPACE, 0xCF, 8, 1); 123 | irsend.sendData(ONEMARK, ONESPACE, ZEROMARK, ZEROSPACE, 0x00, 8, 1); 124 | irsend.sendData(ONEMARK, ONESPACE, ZEROMARK, ZEROSPACE, 0xA0, 8, 1); 125 | irsend.sendData(ONEMARK, ONESPACE, ZEROMARK, ZEROSPACE, 0xC0, 8, 1); 126 | irsend.sendData(ONEMARK, ONESPACE, ZEROMARK, ZEROSPACE, 0xE0, 8, 1); 127 | irsend.sendData(ONEMARK, ONESPACE, ZEROMARK, ZEROSPACE, 0xC5, 8, 1); 128 | irsend.sendData(ONEMARK, ONESPACE, ZEROMARK, ZEROSPACE, 0xE6, 8, 1); 129 | irsend.sendData(ONEMARK, ONESPACE, ZEROMARK, ZEROSPACE, 0xDF, 8, 1); 130 | irsend.sendData(ONEMARK, ONESPACE, ZEROMARK, ZEROSPACE, 0x7F, 8, 1); 131 | } 132 | 133 | void setup() { 134 | 135 | pinMode(GPIO_LED, OUTPUT); 136 | 137 | Blynk.begin(auth, ssid, pass); 138 | // You can also specify server: 139 | //Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 8442); 140 | //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8442); 141 | 142 | // Begin synchronizing time 143 | rtc.begin(); 144 | timer.setInterval(10000L, clockDisplay); 145 | 146 | led1.off(); 147 | irsend.begin(); 148 | 149 | Serial.begin(115200); // Debug console 150 | } 151 | 152 | void loop() { 153 | 154 | Blynk.run(); 155 | timer.run(); 156 | 157 | //Checks if it's time to turn on or off the AC 158 | if (((startHour == currentHour && startMin == currentMin)||(Button)) && AC_ON == false) { 159 | digitalWrite(GPIO_LED, HIGH); 160 | startHour = RESET; startMin = RESET; 161 | led1.on(); 162 | sendIR(); //sendIR();//sends IR start/stop command 163 | Serial.println("IR command sent"); 164 | AC_ON = true; 165 | delay(200); 166 | } 167 | else if (((stopHour == currentHour && stopMin == currentMin)||(Button)) && AC_ON == true) { 168 | digitalWrite(GPIO_LED, LOW); 169 | stopHour = RESET; stopMin = RESET; 170 | led1.off(); 171 | sendIR(); //sendIR();//sends IR start/stop command 172 | Serial.println("IR command sent"); 173 | AC_ON = false; 174 | delay(200); 175 | } 176 | 177 | 178 | } 179 | -------------------------------------------------------------------------------- /BlynkIR/src/IRsend.h: -------------------------------------------------------------------------------- 1 | // Copyright 2009 Ken Shirriff 2 | // Copyright 2015 Mark Szabo 3 | // Copyright 2017 David Conran 4 | #ifndef IRSEND_H_ 5 | #define IRSEND_H_ 6 | 7 | #define __STDC_LIMIT_MACROS 8 | #include 9 | //#include "IRremoteESP8266.h" 10 | 11 | // Originally from https://github.com/shirriff/Arduino-IRremote/ 12 | // Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for 13 | // sending IR code on ESP8266 14 | 15 | #if TEST || UNIT_TEST 16 | #define VIRTUAL virtual 17 | #else 18 | #define VIRTUAL 19 | #endif 20 | 21 | // Constants 22 | // Offset (in microseconds) to use in Period time calculations to account for 23 | // code excution time in producing the software PWM signal. 24 | // Value determined in https://github.com/markszabo/IRremoteESP8266/issues/62 25 | #define PERIOD_OFFSET -3 26 | #define DUTY_DEFAULT 50 27 | 28 | // Classes 29 | class IRsend { 30 | public: 31 | explicit IRsend(uint16_t IRsendPin, bool inverted = false); 32 | void begin(); 33 | void enableIROut(uint32_t freq, uint8_t duty = DUTY_DEFAULT); 34 | VIRTUAL uint16_t mark(uint16_t usec); 35 | VIRTUAL void space(uint32_t usec); 36 | void calibrate(uint16_t hz = 38000U); 37 | void sendRaw(uint16_t buf[], uint16_t len, uint16_t hz); 38 | void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, 39 | uint32_t zerospace, uint64_t data, uint16_t nbits, 40 | bool MSBfirst = true); 41 | void send(uint16_t type, uint64_t data, uint16_t nbits); 42 | #if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO) 43 | void sendNEC(uint64_t data, uint16_t nbits = NEC_BITS, uint16_t repeat = 0); 44 | uint32_t encodeNEC(uint16_t address, uint16_t command); 45 | #endif 46 | #if SEND_SONY 47 | // sendSony() should typically be called with repeat=2 as Sony devices 48 | // expect the code to be sent at least 3 times. (code + 2 repeats = 3 codes) 49 | // Legacy use of this procedure was to only send a single code so call it with 50 | // repeat=0 for backward compatibility. As of v2.0 it defaults to sending 51 | // a Sony command that will be accepted be a device. 52 | void sendSony(uint64_t data, uint16_t nbits = SONY_20_BITS, 53 | uint16_t repeat = SONY_MIN_REPEAT); 54 | uint32_t encodeSony(uint16_t nbits, uint16_t command, uint16_t address, 55 | uint16_t extended = 0); 56 | #endif 57 | #if SEND_SHERWOOD 58 | void sendSherwood(uint64_t data, uint16_t nbits = SHERWOOD_BITS, 59 | uint16_t repeat = SHERWOOD_MIN_REPEAT); 60 | #endif 61 | #if SEND_SAMSUNG 62 | void sendSAMSUNG(uint64_t data, uint16_t nbits = SAMSUNG_BITS, 63 | uint16_t repeat = 0); 64 | uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command); 65 | #endif 66 | #if SEND_LG 67 | void sendLG(uint64_t data, uint16_t nbits = LG_BITS, uint16_t repeat = 0); 68 | uint32_t encodeLG(uint16_t address, uint16_t command); 69 | #endif 70 | #if (SEND_SHARP || SEND_DENON) 71 | uint32_t encodeSharp(uint16_t address, uint16_t command, 72 | uint16_t expansion = 1, uint16_t check = 0, 73 | bool MSBfirst = false); 74 | void sendSharp(uint16_t address, uint16_t command, 75 | uint16_t nbits = SHARP_BITS, uint16_t repeat = 0); 76 | void sendSharpRaw(uint64_t data, uint16_t nbits = SHARP_BITS, 77 | uint16_t repeat = 0); 78 | #endif 79 | #if SEND_JVC 80 | void sendJVC(uint64_t data, uint16_t nbits = JVC_BITS, uint16_t repeat = 0); 81 | uint16_t encodeJVC(uint8_t address, uint8_t command); 82 | #endif 83 | #if SEND_DENON 84 | void sendDenon(uint64_t data, uint16_t nbits = DENON_BITS, 85 | uint16_t repeat = 0); 86 | #endif 87 | #if SEND_SANYO 88 | uint64_t encodeSanyoLC7461(uint16_t address, uint8_t command); 89 | void sendSanyoLC7461(uint64_t data, uint16_t nbits = SANYO_LC7461_BITS, 90 | uint16_t repeat = 0); 91 | #endif 92 | #if SEND_DISH 93 | // sendDISH() should typically be called with repeat=3 as DISH devices 94 | // expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes) 95 | // Legacy use of this procedure was only to send a single code 96 | // so use repeat=0 for backward compatibility. 97 | void sendDISH(uint64_t data, uint16_t nbits = DISH_BITS, 98 | uint16_t repeat = DISH_MIN_REPEAT); 99 | #endif 100 | #if (SEND_PANASONIC || SEND_DENON) 101 | void sendPanasonic64(uint64_t data, uint16_t nbits = PANASONIC_BITS, 102 | uint16_t repeat = 0); 103 | void sendPanasonic(uint16_t address, uint32_t data, 104 | uint16_t nbits = PANASONIC_BITS, uint16_t repeat = 0); 105 | uint64_t encodePanasonic(uint16_t manufacturer, uint8_t device, 106 | uint8_t subdevice, uint8_t function); 107 | #endif 108 | #if SEND_RC5 109 | void sendRC5(uint64_t data, uint16_t nbits = RC5X_BITS, uint16_t repeat = 0); 110 | uint16_t encodeRC5(uint8_t address, uint8_t command, 111 | bool key_released = false); 112 | uint16_t encodeRC5X(uint8_t address, uint8_t command, 113 | bool key_released = false); 114 | uint64_t toggleRC5(uint64_t data); 115 | #endif 116 | #if SEND_RC6 117 | void sendRC6(uint64_t data, uint16_t nbits = RC6_MODE0_BITS, 118 | uint16_t repeat = 0); 119 | uint64_t encodeRC6(uint32_t address, uint8_t command, 120 | uint16_t mode = RC6_MODE0_BITS); 121 | uint64_t toggleRC6(uint64_t data, uint16_t nbits = RC6_MODE0_BITS); 122 | #endif 123 | #if SEND_RCMM 124 | void sendRCMM(uint64_t data, uint16_t nbits = RCMM_BITS, uint16_t repeat = 0); 125 | #endif 126 | #if SEND_COOLIX 127 | void sendCOOLIX(uint64_t data, uint16_t nbits = COOLIX_BITS, 128 | uint16_t repeat = 0); 129 | #endif 130 | #if SEND_WHYNTER 131 | void sendWhynter(uint64_t data, uint16_t nbits = WHYNTER_BITS, 132 | uint16_t repeat = 0); 133 | #endif 134 | #if SEND_MITSUBISHI 135 | void sendMitsubishi(uint64_t data, uint16_t nbits = MITSUBISHI_BITS, 136 | uint16_t repeat = MITSUBISHI_MIN_REPEAT); 137 | #endif 138 | #if SEND_MITSUBISHI_AC 139 | void sendMitsubishiAC(unsigned char data[], 140 | uint16_t nbytes = MITSUBISHI_AC_STATE_LENGTH, 141 | uint16_t repeat = MITSUBISHI_AC_MIN_REPEAT); 142 | #endif 143 | #if SEND_GLOBALCACHE 144 | void sendGC(uint16_t buf[], uint16_t len); 145 | #endif 146 | #if SEND_KELVINATOR 147 | void sendKelvinator(unsigned char data[], 148 | uint16_t nbytes = KELVINATOR_STATE_LENGTH, 149 | uint16_t repeat = 0); 150 | #endif 151 | #if SEND_DAIKIN 152 | void sendDaikin(unsigned char data[], 153 | uint16_t nbytes = DAIKIN_COMMAND_LENGTH, 154 | uint16_t repeat = 0); 155 | #endif 156 | #if SEND_AIWA_RC_T501 157 | void sendAiwaRCT501(uint64_t data, uint16_t nbits = AIWA_RC_T501_BITS, 158 | uint16_t repeat = AIWA_RC_T501_MIN_REPEAT); 159 | #endif 160 | #if SEND_GREE 161 | void sendGree(uint64_t data, uint16_t nbits = GREE_BITS, uint16_t repeat = 0); 162 | void sendGree(uint8_t data[], uint16_t nbytes = GREE_STATE_LENGTH, 163 | uint16_t repeat = 0); 164 | #endif 165 | 166 | protected: 167 | #ifdef UNIT_TEST 168 | #ifndef HIGH 169 | #define HIGH 0x1 170 | #endif 171 | #ifndef LOW 172 | #define LOW 0x0 173 | #endif 174 | #endif // UNIT_TEST 175 | uint8_t outputOn; 176 | uint8_t outputOff; 177 | 178 | private: 179 | uint16_t onTimePeriod; 180 | uint16_t offTimePeriod; 181 | uint16_t IRpin; 182 | int8_t periodOffset; 183 | void ledOff(); 184 | uint32_t calcUSecPeriod(uint32_t hz); 185 | }; 186 | 187 | #endif // IRSEND_H_ 188 | -------------------------------------------------------------------------------- /BlynkIR/src/IRsend.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2009 Ken Shirriff 2 | // Copyright 2015 Mark Szabo 3 | // Copyright 2017 David Conran 4 | 5 | #include "IRsend.h" 6 | #ifndef UNIT_TEST 7 | #include 8 | #else 9 | #define __STDC_LIMIT_MACROS 10 | #include 11 | #endif 12 | #include 13 | #ifdef UNIT_TEST 14 | #include 15 | #endif 16 | #include "IRtimer.h" 17 | 18 | // Originally from https://github.com/shirriff/Arduino-IRremote/ 19 | // Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for 20 | // sending IR code on ESP8266 21 | // Ported to ESP32 by luclala (https://github.com/luclala/IRremoteESP32) 22 | 23 | // IRsend ---------------------------------------------------------------------- 24 | // Create an IRsend object. 25 | // 26 | // Args: 27 | // IRsendPin: Which GPIO pin to use when sending an IR command. 28 | // inverted: *DANGER* Optional flag to invert the output. (default = false) 29 | // e.g. LED is illuminated when GPIO is LOW rather than HIGH. 30 | // Setting this to something other than the default could 31 | // easily destroy your IR LED if you are overdriving it. 32 | // Unless you *REALLY* know what you are doing, don't change this. 33 | // Returns: 34 | // An IRsend object. 35 | IRsend::IRsend(uint16_t IRsendPin, bool inverted) : IRpin(IRsendPin), 36 | periodOffset(PERIOD_OFFSET) { 37 | if (inverted) { 38 | outputOn = LOW; 39 | outputOff = HIGH; 40 | } else { 41 | outputOn = HIGH; 42 | outputOff = LOW; 43 | } 44 | } 45 | 46 | // Enable the pin for output. 47 | void IRsend::begin() { 48 | #ifndef UNIT_TEST 49 | pinMode(IRpin, OUTPUT); 50 | #endif 51 | } 52 | 53 | // Turn off the IR LED. 54 | void IRsend::ledOff() { 55 | #ifndef UNIT_TEST 56 | digitalWrite(IRpin, outputOff); 57 | #endif 58 | } 59 | 60 | // Calculate the period for a given frequency. (T = 1/f) 61 | // 62 | // Args: 63 | // freq: Frequency in Hz. 64 | // Returns: 65 | // nr. of uSeconds. 66 | uint32_t IRsend::calcUSecPeriod(uint32_t hz) { 67 | if (hz == 0) hz = 1; // Avoid Zero hz. Divide by Zero is nasty. 68 | uint32_t period = (1000000UL + hz/2) / hz; // The equiv of round(1000000/hz). 69 | // Apply the offset and ensure we don't result in a <= 0 value. 70 | return std::max((uint32_t) 1, period + periodOffset); 71 | } 72 | 73 | // Set the output frequency modulation and duty cycle. 74 | // 75 | // Args: 76 | // freq: The freq we want to modulate at. Assumes < 1000 means kHz else Hz. 77 | // duty: Percentage duty cycle of the LED. e.g. 25 = 25% = 1/4 on, 3/4 off. 78 | // 79 | // Note: 80 | // Integer timing functions & math mean we can't do fractions of 81 | // microseconds timing. Thus minor changes to the freq & duty values may have 82 | // limited effect. You've been warned. 83 | void IRsend::enableIROut(uint32_t freq, uint8_t duty) { 84 | // Can't have more than 100% duty cycle. 85 | duty = std::min(duty, (uint8_t) 100); 86 | if (freq < 1000) // Were we given kHz? Supports the old call usage. 87 | freq *= 1000; 88 | uint32_t period = calcUSecPeriod(freq); 89 | // Nr. of uSeconds the LED will be on per pulse. 90 | onTimePeriod = (period * duty) / 100; 91 | // Nr. of uSeconds the LED will be off per pulse. 92 | offTimePeriod = period - onTimePeriod; 93 | } 94 | 95 | // Modulate the IR LED for the given period (usec) and at the duty cycle set. 96 | // 97 | // Args: 98 | // usec: The period of time to modulate the IR LED for, in microseconds. 99 | // Returns: 100 | // Nr. of pulses actually sent. 101 | // 102 | // Note: 103 | // The ESP8266 has no good way to do hardware PWM, so we have to do it all 104 | // in software. There is a horrible kludge/brilliant hack to use the second 105 | // serial TX line to do fairly accurate hardware PWM, but it is only 106 | // available on a single specific GPIO and only available on some modules. 107 | // e.g. It's not available on the ESP-01 module. 108 | // Hence, for greater compatibility & choice, we don't use that method. 109 | // Ref: 110 | // https://www.analysir.com/blog/2017/01/29/updated-esp8266-nodemcu-backdoor-upwm-hack-for-ir-signals/ 111 | uint16_t IRsend::mark(uint16_t usec) { 112 | uint16_t counter = 0; 113 | IRtimer usecTimer = IRtimer(); 114 | // Cache the time taken so far. This saves us calling time, and we can be 115 | // assured that we can't have odd math problems. i.e. unsigned under/overflow. 116 | uint32_t elapsed = usecTimer.elapsed(); 117 | 118 | while (elapsed < usec) { // Loop until we've met/exceeded our required time. 119 | #ifndef UNIT_TEST 120 | digitalWrite(IRpin, outputOn); // Turn the LED on. 121 | // Calculate how long we should pulse on for. 122 | // e.g. Are we to close to the end of our requested mark time (usec)? 123 | delayMicroseconds(std::min((uint32_t) onTimePeriod, usec - elapsed)); 124 | digitalWrite(IRpin, outputOff); // Turn the LED off. 125 | #endif 126 | counter++; 127 | if (elapsed + onTimePeriod >= usec) 128 | return counter; // LED is now off & we've passed our allotted time. 129 | // Wait for the lesser of the rest of the duty cycle, or the time remaining. 130 | #ifndef UNIT_TEST 131 | delayMicroseconds(std::min(usec - elapsed - onTimePeriod, 132 | (uint32_t) offTimePeriod)); 133 | #endif 134 | elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time. 135 | } 136 | return counter; 137 | } 138 | 139 | // Turn the pin (LED) off for a given time. 140 | // Sends an IR space for the specified number of microseconds. 141 | // A space is no output, so the PWM output is disabled. 142 | // 143 | // Args: 144 | // time: Time in microseconds (us). 145 | void IRsend::space(uint32_t time) { 146 | ledOff(); 147 | if (time == 0) return; 148 | #ifndef UNIT_TEST 149 | // delayMicroseconds is only accurate to 16383us. 150 | // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds 151 | if (time <= 16383) { 152 | delayMicroseconds(time); 153 | } else { 154 | // Invoke a delay(), where possible, to avoid triggering the WDT. 155 | delay(time / 1000UL); // Delay for as many whole milliseconds as we can. 156 | // Delay the remaining sub-millisecond. 157 | delayMicroseconds(static_cast(time % 1000UL)); 158 | } 159 | #endif 160 | } 161 | 162 | // Calculate & set any offsets to account for execution times. 163 | // 164 | // Args: 165 | // hz: The frequency to calibrate at >= 1000Hz. Default is 38000Hz. 166 | // 167 | // Status: ALPHA / Untested. 168 | // 169 | // NOTE: 170 | // This will generate an 65535us mark() IR LED signal. 171 | // This only needs to be called once, if at all. 172 | void IRsend::calibrate(uint16_t hz) { 173 | if (hz < 1000) // Were we given kHz? Supports the old call usage. 174 | hz *= 1000; 175 | periodOffset = 0; // Turn off any existing offset while we calibrate. 176 | enableIROut(hz); 177 | IRtimer usecTimer = IRtimer(); // Start a timer *just* before we do the call. 178 | uint16_t pulses = mark(UINT16_MAX); // Generate a PWM of 65,535 us. (Max.) 179 | uint32_t timeTaken = usecTimer.elapsed(); // Record the time it took. 180 | // While it shouldn't be neccesary, assume at least 1 pulse, to avoid a 181 | // divide by 0 situation. 182 | pulses = std::max(pulses, (uint16_t) 1U); 183 | uint32_t calcPeriod = calcUSecPeriod(hz); // e.g. @38kHz it should be 26us. 184 | // Assuming 38kHz for the example calculations: 185 | // In a 65535us pulse, we should have 2520.5769 pulses @ 26us periods. 186 | // e.g. 65535.0us / 26us = 2520.5769 187 | // This should have caused approx 2520 loops through the main loop in mark(). 188 | // The average over that many interations should give us a reasonable 189 | // approximation at what offset we need to use to account for instruction 190 | // execution times. 191 | // 192 | // Calculate the actual period from the actual time & the actual pulses 193 | // generated. 194 | double_t actualPeriod = (double_t) timeTaken / (double_t) pulses; 195 | // Store the difference between the actual time per period vs. calculated. 196 | periodOffset = (int8_t) ((double_t) calcPeriod - actualPeriod); 197 | } 198 | 199 | // Generic method for sending data that is common to most protocols. 200 | // Will send leading or trailing 0's if the nbits is larger than the number 201 | // of bits in data. 202 | // 203 | // Args: 204 | // onemark: Nr. of usecs for the led to be pulsed for a '1' bit. 205 | // onespace: Nr. of usecs for the led to be fully off for a '1' bit. 206 | // zeromark: Nr. of usecs for the led to be pulsed for a '0' bit. 207 | // zerospace: Nr. of usecs for the led to be fully off for a '0' bit. 208 | // data: The data to be transmitted. 209 | // nbits: Nr. of bits of data to be sent. 210 | // MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order. 211 | void IRsend::sendData(uint16_t onemark, uint32_t onespace, 212 | uint16_t zeromark, uint32_t zerospace, 213 | uint64_t data, uint16_t nbits, bool MSBfirst) { 214 | if (nbits == 0) // If we are asked to send nothing, just return. 215 | return; 216 | if (MSBfirst) { // Send the MSB first. 217 | // Send 0's until we get down to a bit size we can actually manage. 218 | while (nbits > sizeof(data) * 8) { 219 | mark(zeromark); 220 | space(zerospace); 221 | nbits--; 222 | } 223 | // Send the supplied data. 224 | for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) 225 | if (data & mask) { // Send a 1 226 | mark(onemark); 227 | space(onespace); 228 | } else { // Send a 0 229 | mark(zeromark); 230 | space(zerospace); 231 | } 232 | } else { // Send the Least Significant Bit (LSB) first / MSB last. 233 | for (uint16_t bit = 0; bit < nbits; bit++, data >>= 1) 234 | if (data & 1) { // Send a 1 235 | mark(onemark); 236 | space(onespace); 237 | } else { // Send a 0 238 | mark(zeromark); 239 | space(zerospace); 240 | } 241 | } 242 | } 243 | 244 | // Send a raw IRremote message. 245 | // 246 | // Args: 247 | // buf: An array of uint16_t's that has microseconds elements. 248 | // len: Nr. of elements in the buf[] array. 249 | // hz: Frequency to send the message at. (kHz < 1000; Hz >= 1000) 250 | // 251 | // Status: STABLE / Known working. 252 | // 253 | // Notes: 254 | // Even elements are Mark times (On), Odd elements are Space times (Off). 255 | // 256 | // Ref: 257 | // examples/IRrecvDumpV2/IRrecvDumpV2.ino 258 | void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) { 259 | // Set IR carrier frequency 260 | enableIROut(hz); 261 | for (uint16_t i = 0; i < len; i++) { 262 | if (i & 1) { // Odd bit. 263 | space(buf[i]); 264 | } else { // Even bit. 265 | mark(buf[i]); 266 | } 267 | } 268 | ledOff(); // We potentially have ended with a mark(), so turn of the LED. 269 | } 270 | 271 | #ifndef UNIT_TEST 272 | void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { 273 | switch (type) { 274 | #if SEND_NEC 275 | case NEC: sendNEC(data, nbits); break; 276 | #endif 277 | #if SEND_SONY 278 | case SONY: sendSony(data, nbits); break; 279 | #endif 280 | #if SEND_RC5 281 | case RC5: sendRC5(data, nbits); break; 282 | #endif 283 | #if SEND_RC6 284 | case RC6: sendRC6(data, nbits); break; 285 | #endif 286 | #if SEND_DISH 287 | case DISH: sendDISH(data, nbits); break; 288 | #endif 289 | #if SEND_JVC 290 | case JVC: sendJVC(data, nbits); break; 291 | #endif 292 | #if SEND_SAMSUNG 293 | case SAMSUNG: sendSAMSUNG(data, nbits); break; 294 | #endif 295 | #if SEND_LG 296 | case LG: sendLG(data, nbits); break; 297 | #endif 298 | #if SEND_WHYNTER 299 | case WHYNTER: sendWhynter(data, nbits); break; 300 | #endif 301 | #if SEND_COOLIX 302 | case COOLIX: sendCOOLIX(data, nbits); break; 303 | #endif 304 | #if SEND_DENON 305 | case DENON: sendDenon(data, nbits); break; 306 | #endif 307 | #if SEND_SHERWOOD 308 | case SHERWOOD: sendSherwood(data, nbits); break; 309 | #endif 310 | #if SEND_RCMM 311 | case RCMM: sendRCMM(data, nbits); break; 312 | #endif 313 | #if SEND_MITSUBISHI 314 | case MITSUBISHI: sendMitsubishi(data, nbits); break; 315 | #endif 316 | #if SEND_SHARP 317 | case SHARP: sendSharpRaw(data, nbits); break; 318 | #endif 319 | #if SEND_AIWA_RC_T501 320 | case AIWA_RC_T501: sendAiwaRCT501(data, nbits); break; 321 | #endif 322 | } 323 | } 324 | #endif 325 | --------------------------------------------------------------------------------