ESP32 and IR Control
Posted: Tue Sep 19, 2017 5:49 pm
After a short dalliance with the ESP8266 as an update to Arduino Uno based boards, I have moved on almost completely to the ESP 32. While a bit more costly in single units, the ESP32 development boards are already readily available for under $10 -- not a barrier for a faster, more capable board =incorporating WiFi, USB programing port, Bluetooth, software assignments of almost all ports etc. Here is a chart comparing the two. I was moving to more a more capable platform and would just as soon jump to the superior ESP32 as to invest heavily in the chip it is replacing. Already over a year in the market, the pace of development of the ESP32 in the Maker community and the support from Espressif. The ESP32 Forum
has over 11,000 posts to date. I am here because moving to the ESP32 entailed, for me, porting my IR control efforts to the ESP32. Seeking information on doing this showed me the versatility of the board. I have successfully generated IR control codes with modulated IR carrier frequencies using at least three different approaches and I have one more to try. I have used an interrupt driven approach based on the internal timers of the ESP32, adapted that to use the LED specialty control hardware systems and am planning on using the dedicated IR control hardware baked into the silicone as well. The latter capabilities are not yet as well documented or ported to the Arduino environment I use, so I have left to last. The third approach I have just completed involves application of the UART hack as recently featured here at AnalysIR. The ESP32 has three UART subsystems readily exposed to the programmer. The typical Serial.begin() type access allows the TX0, RX0 use but this is also the conduit for the USB to computer interface used in programming. The TX1, RX1 interface is devoted to onboard flash storage. The TX2/RX2 UART is readily available for the IR Hack.
The Update to the UART Hack of the ESP8266 provided guidance to the port to the ESP32. The UART control registers are fully documented, if barely intelligible to an amateur. The ESP8266 had a key register bit that allows the UART signal to be inverted and, as anticipated, the same register has a bit to invert the signals on the ESP32. Access to this is gained by this include: #include "soc/uart_reg.h"
The register is set in a manner similar to that described in the ESP8266 article:
The index of (UART_CONF0_REG(X)...) above is set to 2 for TX2 as output or changed to 0 if using Serial.write on UART 0.
I also include #include <HardwareSerial.h> although this is part of the core, but serves as a reminder of where to reference the code for use of UART #2. While UART #0 is always accessible, you must initialize the serial port for UART #2 as well as then run a begin command to set up the BAUD rate.
The Arduino routines default to the 8N1 protocol the hack expects. The non-Arduino ESPressiff SDK exposes much more of the details of the UART control, but this was not needed to achieve the UART based IR transmitter. Here is the code for initializing the class
If you intend to also use regular Serial.print functionality, initialize UART0 with a regular Serial.begin(112500); statement before you do the IR UART2 ie IRSerial2.begin(carrierFreq *10).
I had some issues with this ESP32 port. The ESP32 Arduino core (at least at the moment) seems to have troubles with mixed types and math. While you often get away with mixing integers with longs, small signed longs with unsigned longs in calculations, this might well have been the source of my issues. I strongly recommend being careful with number types and declarations. Since memory is more plentiful and speed is decent with the ESP32, I stuck (finally) with unsigned longs for my IR control program. I also had to re-write the basic sending routines in order to achieve proper timing. I think my resultant code, which sends each MARK with its companion SPACE is easily understood and less complex than the use of separate MARK and SPACE routines. I could not achieve robust timing with the latter. I will be posting on the ESP32 Forum regarding some of the issues with timing I observed, but the demo program here works well for Sony, Samsung and NEC control codes and can be extended, I am sure.
Stick to 41,000 Hz plus or minus a couple thousand for best results. If my sample code will not fit here, I will add as an attachment.
My sendMARKSPACE routine should work well for other MCU boardsand the UART hack approach -- it only requires a UART and the micros() function.).
I have attached a basic demo Arduino ESP 32 sketch outputting Sony, Samsung and NEC codes. I have included Mute or power codes so you can see it work w your device. I am attaching an AnalysIR session showing the output of the program in AnalysIR. I could not have accomplished this without the AnalysIR software and there A.IR Shield RX ($10 only!) to capture not only the output but to check my carrier frequency during early trials.
As always,YMMV.
has over 11,000 posts to date. I am here because moving to the ESP32 entailed, for me, porting my IR control efforts to the ESP32. Seeking information on doing this showed me the versatility of the board. I have successfully generated IR control codes with modulated IR carrier frequencies using at least three different approaches and I have one more to try. I have used an interrupt driven approach based on the internal timers of the ESP32, adapted that to use the LED specialty control hardware systems and am planning on using the dedicated IR control hardware baked into the silicone as well. The latter capabilities are not yet as well documented or ported to the Arduino environment I use, so I have left to last. The third approach I have just completed involves application of the UART hack as recently featured here at AnalysIR. The ESP32 has three UART subsystems readily exposed to the programmer. The typical Serial.begin() type access allows the TX0, RX0 use but this is also the conduit for the USB to computer interface used in programming. The TX1, RX1 interface is devoted to onboard flash storage. The TX2/RX2 UART is readily available for the IR Hack.
The Update to the UART Hack of the ESP8266 provided guidance to the port to the ESP32. The UART control registers are fully documented, if barely intelligible to an amateur. The ESP8266 had a key register bit that allows the UART signal to be inverted and, as anticipated, the same register has a bit to invert the signals on the ESP32. Access to this is gained by this include: #include "soc/uart_reg.h"
The register is set in a manner similar to that described in the ESP8266 article:
Code: Select all
#define SET_PERI_REG_MASK(reg, mask) WRITE_PERI_REG((reg), (READ_PERI_REG(reg)|(mask))) // similar but not identical to usage for ESP 8266 Inverts signal from off = high to off = low
SET_PERI_REG_MASK(UART_CONF0_REG(2) , UART_TXD_INV);
I also include #include <HardwareSerial.h> although this is part of the core, but serves as a reminder of where to reference the code for use of UART #2. While UART #0 is always accessible, you must initialize the serial port for UART #2 as well as then run a begin command to set up the BAUD rate.
The Arduino routines default to the 8N1 protocol the hack expects. The non-Arduino ESPressiff SDK exposes much more of the details of the UART control, but this was not needed to achieve the UART based IR transmitter. Here is the code for initializing the class
Code: Select all
HardwareSerial IRSerial2 (2); // set ups IRSerial2 as a Hardware serial port on UART26
pinMode(TX, OUTPUT);
digitalWrite(TX, LOW);
IRSerial2.begin(carrierFreq *10); //carrierFreq is unsigned long and holds the resultant carrier freq ie 38000 in this code
#define SET_PERI_REG_MASK(reg, mask) WRITE_PERI_REG((reg), (READ_PERI_REG(reg)|(mask))) // similar but not identical to usage for ESP 8266 Inverts signal from off = high to off = low
SET_PERI_REG_MASK(UART_CONF0_REG(2) , UART_TXD_INV);
I had some issues with this ESP32 port. The ESP32 Arduino core (at least at the moment) seems to have troubles with mixed types and math. While you often get away with mixing integers with longs, small signed longs with unsigned longs in calculations, this might well have been the source of my issues. I strongly recommend being careful with number types and declarations. Since memory is more plentiful and speed is decent with the ESP32, I stuck (finally) with unsigned longs for my IR control program. I also had to re-write the basic sending routines in order to achieve proper timing. I think my resultant code, which sends each MARK with its companion SPACE is easily understood and less complex than the use of separate MARK and SPACE routines. I could not achieve robust timing with the latter. I will be posting on the ESP32 Forum regarding some of the issues with timing I observed, but the demo program here works well for Sony, Samsung and NEC control codes and can be extended, I am sure.
Stick to 41,000 Hz plus or minus a couple thousand for best results. If my sample code will not fit here, I will add as an attachment.
My sendMARKSPACE routine should work well for other MCU boardsand the UART hack approach -- it only requires a UART and the micros() function.).
I have attached a basic demo Arduino ESP 32 sketch outputting Sony, Samsung and NEC codes. I have included Mute or power codes so you can see it work w your device. I am attaching an AnalysIR session showing the output of the program in AnalysIR. I could not have accomplished this without the AnalysIR software and there A.IR Shield RX ($10 only!) to capture not only the output but to check my carrier frequency during early trials.
As always,YMMV.
Code: Select all
[code]
/*
* Author: J F Monthony based on AnalysIR via https://www.AnalysIR.com/
*UART IR Control w ESP 32
*Title: ESP32UART_IR_Control_Basic
* Date: Sept 19, 2017
*
* License: Creative Commons, Attribution-NonCommercial-ShareAlike 4.0 International
* http://creativecommons.org/licenses/by-nc-sa/4.0/
*
*
*
* Attribution: Please credit the Author on all media and provide a link to https://www.AnalysIR.com/
*
*
*You must set IRSerial2 as a HardwareSerial type for ESP32
*set using the UART number of the port ie 2
*Works best at carrierFrequency of 41,000 and 41,600
*carrierFreq is now an unsigned long containing the final target frequency for the IR carrier
*
* AnalysIR software and an A.IR Shield RX receiver with frequency detection were invaluable in developing this applicarion
* Allowed tuning of timing and confirmed frequency as well as recognizing format as NEC, Sony etc
*The use of complete series of zeros and then a 1010101010 pattern made it easier to track
*Sending a single NEC code to debug and tune. Many sketch variationscould be tuned to yield a
*good NEC output with a to AnalysIR software but then tried other NEC test codes
*Sending 0x0L transmitts the minimal NEC pattern that was impossible (for me) to send succesfully without a major re-write of the sending functions
*The serial writes that generate the modulated carrier are cramed into the ether and spit out at the baud rate.
*With a long space ie NEC_ONE_SPACE, works out OK, but with NEC_ZERO_SPACE, timing goes bad.
*Each consecutive zero (in 0x0L ie 32 zeros) sent shows a shrinking following MARK
* The output in original AnalysIR routines (which worked fine on my 8266 device) showed progressively shorter Marks
*until one disappeared, leaving a big space. In 32 bit NEC code, 4 to 5 bits disappear to shrinking Marks when sent 0x0L
* in final re-written routine, if a Mark exceeds the partner space by much, multiple repeat zeros will yield missing spaces
* and 2X MARKS. Testing with 0x0L as a code easily detects this. Since a code w a missing space doesn't have the
* required number of bits, always decodes as raw in AnalysIR
*As always, I learned a lot working through to a solution. The re-written sending code allowed me to produce functional IR signals for NEC, SONY and SAMSUNG devices
*Although there was a claim written saying that Serial(2) writes don't return until completed, this doesn't mean they have left
*the building... they accumulate likely in a buffer and are spit out in order but IR signal timing based on when the write routines are done is
*not for the faint of heart. Since the total number of writes needed varries depending on how may ones and zeros are to be output
*there is a quite a few combination to accomodate in a 32 bit code.
*Most routines that went back after MARK shoved the correct number of UART write bits at the Serial port
*and tried to use SPACE duration to send the next space failed particulaely for NEC codes with lots of zeros
*
*In the end, I think my rewritten code is straightforward and simpler. Shove the correct number of UART WRITE bits at the Serial port
*They come out in the requisite MARK period and stop when all "printed" to the IR LED.
*Then just wait till the total time for the MARK + its companion SPACE to run out. This lets the buffered, multicore world the compiler
*creates do its thing getting the MARK right and worked a treat to just wait till the end of the following space before getting the next part of the code
*/
//**************************************************************************************
#include <HardwareSerial.h> // Not finally necessary as an explicit include -- likely in core, but left here to guide users to the appropriate files
#include "soc/uart_reg.h"
# define TX 28 // ESP32 Device pinouts vary. TX2 is pin 28 on WROOM, Wire your LED output transistor to TX2 on your board
// # define TX TX2 // DoIT ESP32 Tx2 from Wroom pin 28 maps to GPIO 17 and to my board pin labeled TX2
// DoIT ESP32 TX pin defined as static constant TX2 in \variants\Doit32\pins.h for my variant
unsigned long sigTime = 0; //used in mark & space functions to keep track of time
unsigned long sigStart = 0; //used to calculate correct length of existing signal, to handle some repeats
unsigned long fudgefactor = 0;
unsigned char DUTY = 0xC3; // 60% duty cycle Start bit 1 1100 0011 Stop 1 = 6 on , 4 off per Write (0xC3)
HardwareSerial IRSerial2 (2); // set ups IRSerial2 as a Hardware serial port on UART26
unsigned long carrierFreq = 41000; // happier with a higher frequency, give it one set to 42600 Good for SONY, NEC and SAMSUNG
//**********************************************Serial(2) init routine*************************************************************************************************
void IRSerial2init (unsigned long localHz)
{
if ((localHz < 30000L)|| (localHz>55000)) // don't need wild frequencies
{
localHz = 41600L; // just use a 1 or other low number to accept current default carrierFreq
}
if (localHz == carrierFreq)
{
return;
}
HardwareSerial IRSerial2 (2);
pinMode(TX, OUTPUT);
digitalWrite(TX, LOW);
#define SET_PERI_REG_MASK(reg, mask) WRITE_PERI_REG((reg), (READ_PERI_REG(reg)|(mask))) // similar but not identical to usage for ESP 8266 Inverts signal from off = high to off = low
SET_PERI_REG_MASK(UART_CONF0_REG(2) , UART_TXD_INV); // can be hard on IR LED to run as OFF = HIGH
unsigned long resetHz = 10 * localHz;
IRSerial2.begin(resetHz);
#define SET_PERI_REG_MASK(reg, mask) WRITE_PERI_REG((reg), (READ_PERI_REG(reg)|(mask))) // similar but not identical to usage for ESP 8266 Inverts signal from off = high to off = low
SET_PERI_REG_MASK(UART_CONF0_REG(2) , UART_TXD_INV); // begin may reset to OFF = HIGH, so can't hurt to set again.
carrierFreq = localHz; // save it
}
//**********************************************SETUP**********************************************
void setup() {
pinMode(TX, OUTPUT);
digitalWrite(TX, LOW);
pinMode(BUILTIN_LED, OUTPUT); // most boards pre-define BUILTIN_LED or LED_BUILTIN or both
Serial.begin(115200); // uncomment to print don't uncomment if you want to try and use Serial.Write for IR output
//Serial.begin(carrierFreq*10); // uncomment to run IR on "Serial" without any Hardware Serial declararion. Use TX0 on your board
IRSerial2.begin((carrierFreq*10));
#define SET_PERI_REG_MASK(reg, mask) WRITE_PERI_REG((reg), (READ_PERI_REG(reg)|(mask))) // similar but not identical to usage for ESP 8266 Inverts signal from off = high to off = low
SET_PERI_REG_MASK(UART_CONF0_REG(2) , UART_TXD_INV);
Serial.println (carrierFreq);
/* The timming of the MARKs on the ESP32 is strange. at a constant 41,000 Hz carrier, one can tune at least for SONY, NEC and SAMSUNG
* default duty cycle of 60% works fine. Too much complexity in the init routines, just change value in sketch if needed.
*
* This will also be a base point for IR using timers and/or the internal RMT hardware (poorly documented especially in Arduino branch)
* I have working timer software, but it uses interrupts and this routine does not so may be complimentary in some applications
* I would like to see if restricting portions of the code to run on a single core would have the timing behaving normally
* On the ESP8266, you can switch to 36,000 Hz carrier, no problems w original AnalysIR UART Hack approach
* Try it here if you want to see it hang!
*/
}
//********************************Begin LOOP*********************************************************************
void loop() { //just send example signals ever 5 seconds at a range of carrier frequencies
sendHexNEC(0xFFFF0000UL,32,1); // good test code for AnalysIR based tuning
// Serial.println(carrierFreq);
delay(2000); //wait 2 seconds between each signal (change to suit)
sendHexSAMSUNG(0xFFFF0000UL); // good test code to look for timing issues
delay(2000);
sendHexNEC(0x4BB4807FUL,32,2); // NEC Power Code, or use 4BB4807F Mute code
delay(2000);
sendHexSAMSUNG(0xE0E0F00FUL); // Samsung Mute E0E0F00F code + UL for unsigned long type or use E0E040BF power code
// Serial.println(carrierFreq);
delay(2000);
sendHexSAMSUNG(0xE0E0E01FUL); // Samsung Vol+ code E0E0E01F
// Serial.println(carrierFreq);
delay(2000);
sendHexSONY(0XA91, 12, 1); // Sony code Power
Serial.println(carrierFreq);
delay(2000);
sendHexSONY(0x100,12,2); // Good Test Pattern
// Serial.println(carrierFreq);
delay(2000);
sendHexSONY(0x7D1, 12, 2 ); // Sony code Disc skip
// Serial.println(carrierFreq);
delay(2000);
}
//********************************************************************************************************************
void sendHexSAMSUNG(unsigned long sigCode)
{
#define SAMSUNG_HEADER_MARK 4560 // 4400....4500....4560 Recorded changes during optimization for recognition by AnaysIR
#define SAMSUNG_HEADER_SPACE 4560 // 4600 ..............4560.....
#define SAMSUNG_ONE_MARK 480 // 450.........500...480
#define SAMSUNG_ZERO_MARK 470 // 480...--> 500...480......470
#define SAMSUNG_ONE_SPACE 1680 // 1640..............................1680
#define SAMSUNG_ZERO_SPACE 620 // 620......600.......620
#define SAMSUNG_TRAILER_MARK 490 // 500............................490
//***************************do some math here not in critical timing loops of Send routines********************
unsigned long SAMSUNG_HEADER_TOTAL = SAMSUNG_HEADER_MARK + SAMSUNG_HEADER_SPACE;
unsigned long SAMSUNG_ONE_TOTAL = SAMSUNG_ONE_MARK + SAMSUNG_ONE_SPACE;
unsigned long SAMSUNG_ZERO_TOTAL = SAMSUNG_ZERO_MARK + SAMSUNG_ZERO_SPACE;
/* All my codes are 32 bit Samsung codes
* A basic 32 bit Samsunf signal is made up of:
* 1 x 4600 uSec Header Mark, followed by
* 1 x 4600 uSec Header Space, followed by
* 32 x data bits all mark bits are 600 micros followed by either 500 uS for 0 bit or 1500-1600 us for 1. Space is either 500 (0) or 1500 (1) ie 1x and 3x
* only have a couple samsung devices so do not know if all follow this standard
* 1 x 500 uSec Trailer Mark
* My Samsung Remote always sends 2 complete codes, with a 108 micros timing. for 32 bit,
* My frequency is 37500 to 37,800 likely good at 40 and for sure at 38
* May vary w the exact frequency you achieve in modulating IR signal.. Again, AnalysIR shows when codes are "Sony" good
*/
unsigned long localHz = 41200; //optimum for my system and AnalysIR
if (localHz !=carrierFreq)
{
IRSerial2init(localHz);
}
unsigned long bitMask = (unsigned long) 1 << (32 - 1); // ALL 32 bits
unsigned long SAMSUNGbitMask = bitMask ; // preserve this for Samsung for use with repeats
sigTime = micros(); //keeps rolling track of signal time to avoid impact of loop & code execution delays
sigStart = sigTime; //remember for calculating first repeat gap (space), must end 108ms after signal starts
unsigned long repeatTimeout = 45000;
// First send header Mark & Space
sendMarkSpace(SAMSUNG_HEADER_MARK,SAMSUNG_HEADER_TOTAL);
sigTime = micros();
while (bitMask) //loop through data bits
{
if (bitMask & sigCode)
{ // its a One bit
sendMarkSpace(SAMSUNG_ONE_MARK,SAMSUNG_ONE_TOTAL);
sigTime = micros();
}
else
{ // its a Zero bit
sendMarkSpace(SAMSUNG_ZERO_MARK,SAMSUNG_ZERO_TOTAL);
sigTime = micros();
}
bitMask = (unsigned long) bitMask >> 1; // shift the mask bit along until it reaches zero & we exit the while loop
}
// **************************************Once Done******************************************
//now repeat the SAMSUNG code completely
/* A repeat signal consists of
* A space which ends 45 microseconds after the START of the previous signal in this sequence
* the same IR code retransmitted, so reset bitmask and calculate timing space
* First calcualte length of space before first repeat
* by getting length of signal to date and subtracting from 45 microseconds (repeatTimeout)
*/
//******first repeat must start 108ms after first signal**** to use sendMarkSpace, get a space to go with SAMSUNG_TRAILER_MARK
unsigned long bigspace((108000 - (sigTime - sigStart))); //first repeat Header should start 108ms after first signal
sendMarkSpace(SAMSUNG_TRAILER_MARK, (SAMSUNG_TRAILER_MARK + bigspace));
sigTime = micros();
sendMarkSpace(SAMSUNG_HEADER_MARK,SAMSUNG_HEADER_TOTAL);
bitMask = SAMSUNGbitMask;
while (bitMask) // loop through data bits
{
if (bitMask & sigCode)
{ // its a One bit
sendMarkSpace(SAMSUNG_ONE_MARK, SAMSUNG_ONE_TOTAL);
sigTime = micros();
}
else
{ // its a Zero bit
sendMarkSpace(SAMSUNG_ZERO_MARK,SAMSUNG_ZERO_TOTAL);
sigTime = micros();
}
bitMask = (unsigned long) bitMask >> 1; // shift the mask bit along until it reaches zero & we exit the while loop
}
sendMarkSpace(SAMSUNG_TRAILER_MARK,bigspace);
Serial.print("Finished one NEC at "); // since we most likely will only ever send one repeat code, not really necessary
Serial.println(carrierFreq);
digitalWrite(BUILTIN_LED,LOW);
}
//***********************************SendHexNEC ********************************************************
void sendHexNEC(unsigned long sigCode, byte numBits, int repeats)
{
digitalWrite(BUILTIN_LED,HIGH); //Power up
/* A basic 32 bit NEC signal is made up of:
* 1 x 9000 uSec Header Mark, followed by
* 1 x 4500 uSec Header Space, followed by
* 32 x bits of data ( 1- bit = 560 uSec Mark followed by 1690 uSec space; 0 - bit= 560 uSec Mark follwed by 560 uSec Space)
* 1 x 560 uSec Trailer Mark
* There can also be a generic repeat signal, which is usually not neccessary & can be replaced by sending multiple signals
* A single repeat signal is sent below, no matter how many are requested
*/
#define NEC_HEADER_MARK 8940UL // 8940
#define NEC_REPEAT_SPACE 2600UL // 2600.........
#define NEC_HEADER_SPACE 4500UL // 4400........4420......4500
#define NEC_ONE_MARK 540UL // 540-->500........................540
#define NEC_ZERO_MARK 540UL // 540.....500.........................540...
#define NEC_ONE_SPACE 1680UL // 1640........................1680
#define NEC_ZERO_SPACE 620UL // 620.
#define NEC_TRAILER_MARK 500UL // 540...... 500......
unsigned long NEC_HEADER_TOTAL = NEC_HEADER_MARK + NEC_HEADER_SPACE;
unsigned long NEC_ONE_TOTAL = NEC_ONE_MARK + NEC_ONE_SPACE;
unsigned long NEC_ZERO_TOTAL = NEC_ZERO_MARK + NEC_ZERO_SPACE;
unsigned long NEC_REPEAT_TOTAL = NEC_HEADER_MARK + NEC_REPEAT_SPACE;
unsigned long localHz = 40500; // reads best on my system
if (localHz !=carrierFreq) // so switch to optimum if needed
{
IRSerial2init(localHz);
}
unsigned long bitMask = (unsigned long) 1 << (numBits - 1); //allows for signal from 1 bit up to 32 bits
sigTime = micros(); //keeps rolling track of signal time to avoid impact of loop & code execution delays
sigStart = sigTime; //remember for calculating first repeat gap (space), must end 108ms after signal starts
sendMarkSpace(NEC_HEADER_MARK, NEC_HEADER_TOTAL); // data starts here
sigTime = micros(); // reset sig time after every MarkSpace pair
while (bitMask)
{
if (bitMask & sigCode) // its a One bit and stock works
{
sendMarkSpace(NEC_ONE_MARK, NEC_ONE_TOTAL);
sigTime = micros(); // prepare for next pair
}
else
{ // its a Zero bit
sendMarkSpace(NEC_ZERO_MARK, NEC_ZERO_TOTAL);
sigTime = micros();
}
bitMask = (unsigned long) bitMask >> 1; // shift the mask bit along until it reaches zero & we exit the while loop
}
//need to add theNEC_ TRAILER_MARK and then send the requested number of NEC repeat signals.
// Repeats can be useful for certain functions like Vol+, Vol- etc but will just always do one repeat code
/* A repeat signal consists of
* A space which ends 108ms after the start of the last signal in this sequence
* 1 x 9000 uSec Repeat Header Mark, followed by
* 1 x 2250 uSec Repeat Header Space, followed by
* 1- bit 560 uSec Mark
* 1 x 560 uSec repeat Trailer Mark
* Wrap up with a trailer mark and a space long enough for a repeat to follow it
* /First calculate length of space for first repeat by getting length of signal to date and subtracting from 108ms
*/
//******first repeat must start 108ms after first signal**** to use sendMarkSpace, get a space to go with NEC_TRAILER_MARK
unsigned long bigspace = ((108000 - (sigTime - sigStart))); //first repeat Header should start 108ms after first signal
sendMarkSpace(NEC_TRAILER_MARK,(NEC_TRAILER_MARK + bigspace));
sigTime = micros();
sigStart = sigTime;
sendMarkSpace(NEC_HEADER_MARK,NEC_REPEAT_TOTAL);
sigTime=micros();
bigspace= (108000 - (sigTime - sigStart));
sendMarkSpace( NEC_TRAILER_MARK,(NEC_TRAILER_MARK + bigspace)); // send Trialer Mark and space to set up for another
Serial.print("Finished one NEC at "); // since we most likely will only ever send one repeat code, not really necessary
Serial.println(carrierFreq);
digitalWrite(BUILTIN_LED,LOW); // End Visual cue. I also used a wire from Pin2 to drive a transistor powered on/power off "switch" on the modulating transistor but not needed
sigTime=micros(); // since we most likely will only ever send one repeat code, not really necessary
}
//**********************************SendHexSONY*************************************************************
void sendHexSONY(unsigned long sigCode, byte numBits, int repeats)
{
#define SONY_HEADER_MARK 2360UL // 2360
#define SONY_HEADER_SPACE 650UL // 650
#define SONY_ONE_MARK 1124UL // 2 x mark 1200 -->1124 better distribution observed is 100% 1200 to 1250
#define SONY_ZERO_MARK 470UL // 490--> 470
#define SONY_ONE_SPACE 670UL // 680....670
#define SONY_ZERO_SPACE 670UL // 680...670
unsigned long SONY_HEADER_TOTAL = SONY_HEADER_MARK + SONY_HEADER_SPACE;
unsigned long SONY_ONE_TOTAL = SONY_ONE_MARK + SONY_ONE_SPACE;
unsigned long SONY_ZERO_TOTAL = SONY_ZERO_MARK + SONY_ZERO_SPACE;
/* All my codes are 12 bit Sony codes
* A basic 12 bit Sony signal is made up of:
* 1 x 2400 uSec Header Mark, followed by
* 1 x 600 uSec Header Space, followed by
* 12 x bits either 1200 uS for 0 bit or 1800 us for 1. Space is always 600us and mark is either 600 (0) or 1200 (1)
* Longer codes just add bits as line above
* 1 x 500 uSec Trailer Mark
* My Sony Remote always sends 2 more complete codes, with a 45 msec timing. for 12 bit,
* Adjusted all spaces to 610 or multiple thereof and marks to 590 and multiples of to get my particular Sony to read
* May vary w the exact frequency you achieve in modulating IR signal.. Again, AnalysIR shows when codes are "Sony" good
*/
unsigned long SonyBestHz = 41600; // optimum on my system
if (SonyBestHz !=carrierFreq)
{
IRSerial2init(SonyBestHz);
}
unsigned long bitMask = (unsigned long) 1 << (numBits - 1); //allows for signal from 1 bit up to 32 bits
unsigned long SonybitMask = bitMask ; // preserve this for Sony since we send 3 times and decrement bitMask
unsigned long repeatTimeout = 45000;
sigTime = micros(); //keeps rolling track of signal time to avoid impact of loop & code execution delays
sigStart = sigTime; //remember for calculating first repeat gap (space), must end 108ms after signal starts
sendMarkSpace(SONY_HEADER_MARK,SONY_HEADER_TOTAL); // First send header Mark & Space
sigTime = micros();
while (bitMask) // loop through data bits
{
if (bitMask & sigCode)
{ //its a One bit
sendMarkSpace(SONY_ONE_MARK,SONY_ONE_TOTAL);
sigTime = micros();
}
else
{ // its a Zero bit
sendMarkSpace(SONY_ZERO_MARK,SONY_ZERO_TOTAL);
sigTime = micros();
}
bitMask = (unsigned long) bitMask >> 1; // shift the mask bit along until it reaches zero & we exit the while loop
}
// *************************************Once Done******************************************
//now send the requested number of SONY repeat signals.
/* A repeat signal consists of
* A space which ends 45 microseconds after the START of the previous signal in this sequence
* the same IR code retransmitted, so reset bitmask and calculate timing space
* First calcualte length of space before first repeat
* by getting length of signal to date and subtracting from 45 microseconds (repeatTimeout)
*/
while (repeats-- > 0) // now send repeats at 45 microsecond intervals
{
delayMicroseconds(repeatTimeout - (sigTime -sigStart)); // just let the time to the start of any repeats run out, even if no repeat wil be sent
sigTime = micros(); // set up for next repeat sigTime is updated each send
sigStart = sigTime;
sendMarkSpace(SONY_HEADER_MARK, SONY_HEADER_TOTAL);
bitMask = SonybitMask;
sigTime = micros();
sigStart = sigTime;
//loop through data bits
while (bitMask)
{
if (bitMask & sigCode)
{ //its a One bit
sendMarkSpace(SONY_ONE_MARK,SONY_ONE_TOTAL);
sigTime = micros();
}
else
{ // its a Zero bit
sendMarkSpace(SONY_ZERO_MARK,SONY_ZERO_TOTAL);
sigTime = micros();
} ana
bitMask = (unsigned long) bitMask >> 1; // shift the mask bit along until it reaches zero & we exit the while loop
}
}
delay(300); // don't pile them up
Serial.println("Completed Sony Send");
}
//****************************************sendMarkSpace******************************************************************************************************************
void sendMarkSpace(unsigned long internalMark,unsigned long internalTotal)
{
//*******************************Load up the Mark Writes *******************************************
unsigned long cycleCount = (internalMark*carrierFreq)/1000000; // expects carrierFreq to be unsigned long above 30,000 Hz
while (cycleCount)
{
IRSerial2.write(DUTY); // while (true) { //send continuous carrier, for testing, signal generator or just generic PWM
// Serial.write(DUTY); // uncomment for Serial. or TX0, Comment out preceeding IRSerial2.write line above
cycleCount = cycleCount -1; //write a character to emulate carrier, character value determines duty cycle.
}
unsigned long endTime = (sigTime +internalTotal); //
/*
* /We have stuffed them somewhere, now wait till time is up for both the MARK and the requisite following SPACE
* This takes care of the mark, no matter what the lag between finishing the write(Duty) and the actual end of the UART sending
* So now we just wait for the rest of the total time, finishing mark and adding space.
*/
//***************************Now Wait for the endTime to expire**********************************
while (micros()<endTime) // just loop like everyone else does
{
}
}
//*************************************END of CODE***********************************************************