Menu

Rewrite IRremote on Java

TimReset
2015-05-11
2015-06-06
  • TimReset

    TimReset - 2015-05-11

    Good day!
    I tried rewrite code for IR https://github.com/shirriff/Arduino-IRremote
    I write simple worked code on C++:

    #define NEC_HDR_MARK  9000
    #define NEC_HDR_SPACE 4500
    #define NEC_BIT_MARK  560
    #define NEC_ONE_SPACE 1600
    #define NEC_ZERO_SPACE  560
    #define NEC_RPT_SPACE 2250
    
    #define TOPBIT 0x80000000
    
    #define TIMER_PWM_PIN        3
    
    #define INPUT 0x0
    #define OUTPUT 0x1
    
    #define SYSCLOCK F_CPU
    
    const unsigned long SPEAKER_IR_POWER = 2155823295L;
    
    //The setup function is called once at startup of the sketch
    void setup() {
    //  Serial.begin(9600);
    // Add your initialization code here
    }
    
    void TIMER_DISABLE_INTR() {
        TIMSK2 = 0;
    }
    
    void 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);
    }
    
    void TIMER_CONFIG_KHZ(int khz) {
        const uint8_t pwmval = SYSCLOCK / 2000 / (khz);
        TCCR2A = _BV(WGM20);
        TCCR2B = _BV(WGM22) | _BV(CS20);
        OCR2A = pwmval;
        OCR2B = pwmval / 3;
    }
    
    void TIMER_ENABLE_PWM(){
        TCCR2A |= _BV(COM2B1);
    }
    
    void TIMER_DISABLE_PWM(){
        TCCR2A &= ~(_BV(COM2B1));
    }
    
    void 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
      delayMicroseconds(time);
    }
    
    /* Leave pin off for time (given in microseconds) */
    void 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
      delayMicroseconds(time);
    }
    
    void 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);
    }
    
    // The loop function is called in an endless loop
    void loop() {
        delay(2000);
        sendNEC(SPEAKER_IR_POWER, 32);
    }
    

    It's code - copy-paste from IRremote.cpp. I test it's code on my IR speaker and it work.
    Before I rewrite it's code on Java:

    import haiku.arduino.api.ArduinoImpl;
    
    import static haiku.avr.AVRConstants.*;
    import static haiku.avr.lib.arduino.WProgram.*;
    
        private static final long SYSCLOCK = ArduinoImpl.F_CPU;
        // 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
        static int NEC_HDR_MARK = 9000;
        static int NEC_HDR_SPACE = 4500;
        static int NEC_BIT_MARK = 560;
        static int NEC_ONE_SPACE = 1600;
        static int NEC_ZERO_SPACE = 560;
        static int NEC_RPT_SPACE = 2250;
    
        static final long TOPBIT = 0b10000000000000000000000000000000;//max_int
    
        static final long SPEAKER_IR_POWER = 0b10000000011111110100000010111111;
    
        static byte ledPin = 3;
    
        private static void mark(int time) {
            TIMER_ENABLE_PWM();
            //      Arduino.delayMicroseconds(time);
            delayMicroseconds(time);
        }
    
        /* Leave pin off for time (given in microseconds) */
        private static void space(int time) {
            //      digitalWrite(ledPin, LOW);
            TIMER_DISABLE_PWM();
            //      Arduino.delayMicroseconds(time);
            delayMicroseconds(time);
        }
    
        static void sendNEC(long data, int nbits) {
            Serial.println("Send " + data + "; nbits " + nbits);
            enableIROut(38);
            mark(NEC_HDR_MARK);
            space(NEC_HDR_SPACE);
            for (int i = 0; i < nbits; i++) {
                if ((data & TOPBIT) != 0) {
                    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);
        }
    
        static void enableIROut(int khz) {
            TIMER_DISABLE_INTR(); //Timer2 Overflow Interrupt
    
            pinMode(ledPin, OUTPUT);
            digitalWrite(ledPin, LOW);
    
            TIMER_CONFIG_KHZ(khz);
        }
    
        static void TIMER_ENABLE_PWM() {
            TCCR2A |= _BV(COM2B1);
        }
    
        static void TIMER_DISABLE_PWM() {
            TCCR2A &= ~(_BV(COM2B1));
        }
    
        private static void TIMER_CONFIG_KHZ(int khz) {
            int pwmval = (int) SYSCLOCK / 2000 / khz;
            TCCR2A = _BV(WGM20);
            TCCR2B = _BV(WGM22) | _BV(CS20);
            OCR2A = pwmval;
            OCR2B = pwmval / 3;
        }
    
        static void TIMER_DISABLE_INTR() {
            TIMSK2 = 0;
        }
    
        public static void main(String[] args) {
    
            init();
            Serial.begin(57600);
            while (true) {
                sendNEC(SPEAKER_IR_POWER, 32);
                delay(2000);
            }
        }
    

    This code send all bit's for value SPEAKER_IR_POWER but speaker not receive their. I tried use Arduino.delayMicroseconds instead WProgram.delayMicroseconds but it's not helped.
    Do you have any idea what this not worked?
    P.S. And how formating code on this forum?!

     

    Last edit: genom2 2015-05-14
    • genom2

      genom2 - 2015-05-14

      OT begin
      "And how formating code on this forum?!"
      Chris asked me the same question last year. My answer is here:
      https://sourceforge.net/p/haiku-vm/discussion/general/thread/4ac7ed8a/#02d7/fb7a
      OT end

      Again, you gave me to little information:
      a) Which Config are you useing? If not useing arduinoIDE or duemilanove? I'm confused because you write about Arduino.delayMicroseconds() AND WProgram.delayMicroseconds().
      b) Your code uses Java type long. What Config:Mode are you useing: 16/32 or 32/64? Is 16/32 sufficient?
      c) Try to debug with more Serial.println() statements.

       
  • TimReset

    TimReset - 2015-05-15

    a) I use configuration "arduino". I write Arduino.delayMicroseconds() but it's commented code - I tried use configuration "arduinoIDE" and compile and deploy with Arduino IDE but it's not help.

    b) I'm use Mode 16/32. As default mode for config "arduino".

    c) I tried debug out with Serial.print():

        static void sendNEC(long data, int nbits) {
            Serial.println("Send " + data + "; nbits " + nbits);
            enableIROut(38);
            mark(NEC_HDR_MARK);
            space(NEC_HDR_SPACE);
            for (int i = 0; i < nbits; i++) {
                if ((data & TOPBIT) != 0) {
                    Serial.print("one ");
                    mark(NEC_BIT_MARK);
                    space(NEC_ONE_SPACE);
                } else {
                    Serial.print("zero ");
                    mark(NEC_BIT_MARK);
                    space(NEC_ZERO_SPACE);
                }
                data <<= 1;
            }
            mark(NEC_BIT_MARK);
            space(0);
            Serial.println();
        }
    

    It's printing:

    Send -2139144001; nbits 32
    one zero zero zero zero zero zero zero zero one one one one one one one zero one zero zero zero zero zero zero one zero one one one one one one 
    

    It's correct data.

    WProgram.delayMicroseconds() is incorrect delay on microseconds - it's use:

    Thread.nap(us / 1000);
    

    Can I use "pure" (real) delay on microseconds on WProgram? Or I must use Arduino.delayMicroseconds() and compile this code with Arduino IDE? Although it's not helped.

     
  • TimReset

    TimReset - 2015-05-22

    Hello!
    I used oscilloscope for view signal on IR LED. And I found their rules: in C++ code have a uniform signal, but in Java code have a delay without signal. Look like a GC perform. I tried disable GC (used arduino.GC=HAIKU_NoGC in HaikuVM.properties) but it's not helped.
    Do you have any idea what it's the delay?

     
    • genom2

      genom2 - 2015-05-23

      I'm expecting the following sequence of signals on your oscilloscope when sending SPEAKER_IR_POWER:

      mark : 9000
      space: 4500
      mark : 560
      space: 1600
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 560
      mark : 560
      space: 1600
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 560
      mark : 560
      space: 1600
      mark : 560
      space: 560
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 1600
      mark : 560
      space: 0
      

      Where "mark : 560" signals a PWM burst of 560 µs and "space: 1600" signals silence for 1600 µs.
      What does your oscilloscope show you? Where is the delay you mentioned?

      I see three sources of delay:

      1) Garbage Collection
      If setting "arduino.GC=HAIKU_NoGC" then the GC should be turned off.

      #define HAIKU_GC (HAIKU_NoGC)
      

      Please take a look into your project "haikuDefs.h" file. Even if GC is switched on I don't expect a GC delay because in your central for-loop of sendNEC no GC run will be triggered. (It is possible but unlikely.)

      2) Threading
      Because you are not using Threads, HaikuVM should have detect this and should have set:

      #define HAIKU_Threads (0)
      

      Please take a look into your project "haikuDefs.h" file.

      Therefore this piece of code:

      yield:
      #if HAIKU_Threads!=0
          if ((t++&0x3)==0) switchThread();
      #endif
      

      in "bytecodeAsJump.h" should be disabled. In consequence Thread-switching/scheduling is turned off and is no source of delay.

      3) Timer interrupts
      HaikuVM uses timer interrupts to manage time e.g. in System.currentTimeMillis(). See http://haiku-vm.sourceforge.net/#[[Tutorial%20All%20About%20MicroKernels]]

      #define HAIKU_TimerInterrupt (TIMER0_OVF_vect)
      

      Please take a look into your project "haikuDefs.h" file to see this definition.

      The timer 0 interrupt is set up in ArduinoLib.init(). When calling ArduinoLib.init(), like you do in your main() function, then a timer 0 interrupt is a source of delay.

       

      Last edit: genom2 2015-05-23
  • TimReset

    TimReset - 2015-05-23

    Thanks for reply!

    I watched your file:
    in haikuDefs.h

    #define HAIKU_GC (HAIKU_NoGC)
    #define HAIKU_TimerInterrupt (TIMER0_OVF_vect)
    

    and I not found bytecodeAsJump.h file.

    I add the attachment with signal. First column - correct signal, second column - incorrect signal. May be it's helped for analyze.

     

    Last edit: TimReset 2015-05-23
    • genom2

      genom2 - 2015-05-23

      You wrote about delays. I see a delay in the column you marked "CORRECT". This confuses me!

      I think you mixed up "CORRECT" and "INCORRECT"? When looking at your data, I think it is the other way around!? First column should be labeled "INCORRECT".

      Please, send me one whole sweep. This is: all 32 Bits of data including start and end mark.

      What about:

      #define HAIKU_Threads ???
      

      The File "bytecodeAsJump.h" should be in:
      C:/haikuVM/myCProject/HaikuVM/aotVariants/

       

      Last edit: genom2 2015-05-23
  • TimReset

    TimReset - 2015-05-24

    Thanks for quick reply!

    I have in haikuDefs.h

    #define HAIKU_Threads (0)
    

    About delay in csv file. It's right file. Look:
    First column (correct), row 376, have signal. But in second column (incorrect) not have signal and further not have signal.
    Further from row 1005 first row have signal, but incorrect row not have signal.

    I don't know about

    Please, send me one whole sweep. This is: all 32 Bits of data including start and end mark.
    

    I was send all data of oscilloscope - I do capture all signal. And I don't know what I can capture more signal. :(

     

    Last edit: TimReset 2015-05-24
    • genom2

      genom2 - 2015-05-25

      Using delayMicroseconds() of HaikuVM is critical. I mapped it to millisecond resolution, because I found no platform independent way to implement it.

      1) Use delayMicroseconds() of the Arduino IDE library
      So I re-coded your IRremote.java to use delayMicroseconds() of the Arduino IDE. For this I had to:

      a) declare delayMicroseconds() as native and annotate it as @NativeCFunction. (See: http://haiku-vm.sourceforge.net/#[[JNI%20using%20functions%20from%20a%20C%20library%20using%20annotations]])

      b) to use it I have to compile with the Arduino IDE, so I have to haikufy it with configuration arduinoIDEUpload.

      c) as a consequence I have to switch from main() to the setup()-loop()-pair of processing.

      d) Finally, because HaikuVM has interpreter overhead, I have to tune your timing (NEC_BIT_MARK, NEC_ONE_SPACE, etc.) I did my very best to tune it, which is hard without using an oscilloscope.

      I stored the resulting IRremote.java here: https://sourceforge.net/p/haiku-vm/code/HEAD/tree/trunk/gallerie/src/main/java/avr/gallerie/user/TimReset/IRremote.java

      2) Data

      I don't know about

      Please, send me one whole sweep. This is: all 32 Bits of data including start and end mark.

      To see the data I expect, please run (with regular Java not HaikuVM): https://sourceforge.net/p/haiku-vm/code/HEAD/tree/trunk/gallerie/src/main/java/avr/gallerie/user/TimReset/IRremoteExpectedData.java

      3) Sometimes good to know

      Please write with L
                        static final long TOPBIT = 0b10000000000000000000000000000000L;//max_int
      Because in 32/64 (regular Java) without L
                        static final long TOPBIT = 0b10000000000000000000000000000000;//max_int
      TOPBIT will evaluate to
          TOPBIT== 0b1111111111111111111111111111111110000000000000000000000000000000
      which results in unwanted program habit.
      
       

      Last edit: genom2 2015-05-25
      • TimReset

        TimReset - 2015-06-03

        Good day!
        Thanks for answer! Sorry for late response.

        I tried your code https://sourceforge.net/p/haiku-vm/code/HEAD/tree/trunk/gallerie/src/main/java/avr/gallerie/user/TimReset/IRremote.java and it's work!
        I test it's code - undo the code to may example. And I found what important code - it's:
        1) NEC_OFFSET - if I set NEC_OFFSET = 0 and it's not work.
        2) @NativeCFunction
        private static native void delayMicroseconds(int time); - if I used WProgram.delayMicroseconds and it's not work.

        hex and binary representation field is similarly - 0x80000000L and 0b10000000000000000000000000000000;

        But when I use Config arduinoIDEUpload it's not work -

        D:\arduino\HaikuVM-1.3.1\haikuVM\bin\../bin/haikuupload IrTest..hex
        compile and upload using arduino.exe with last port and board settings
        
        /arduino/HaikuVM-1.3.1/haikuVM/bin/../hardware/tools/avr/utils/bin/sh.exe: ./arduino.exe:
        No such file or directory
        make: *** [upload] Error 127
        #############################################################
        # error while uploading files
        #############################################################
        java.lang.Exception: Script 'D:\arduino\HaikuVM-1.3.1\haikuVM\bin\../bin/haikuupload.bat'
        exited with errorcode=2
                at haikuvm.pc.tools.HaikuVM.executeBlocking(HaikuVM.java:545)
                at haikuvm.pc.tools.HaikuVM.call(HaikuVM.java:226)
                at haikuvm.pc.tools.HaikuVM.haikuupload0(HaikuVM.java:206)
                at haikuvm.pc.tools.HaikuVM.haiku0(HaikuVM.java:151)
                at haikuvm.pc.tools.HaikuVM.main(HaikuVM.java:101)
        

        I used Windows 7 and add ArduinoIDE folder to Path.

        I deploy to ArduinoIDE manually - copy HaikuVM folder in ArduinoIDE library folder and deploy from ArduinoIDE.

         

        Last edit: TimReset 2015-06-03
        • genom2

          genom2 - 2015-06-06

          To summarize, it works if:

          1) You let NEC_OFFSET = 204;
          2) You use:

          @NativeCFunction
          private static native void delayMicroseconds(int time);
          

          3) But, when you use Config arduinoIDEUpload it fails to C-compile and upload.
          Please, try this recommended way:

          cd C:\arduino-1.0.5\libraries    
          C:\haikuVM\bin\haiku -v --Config arduinoIDEUpload C:\haikuVM\examples\src\main\java\processing\examples\_01_Basics\Blink.java  
          

          (see http://haiku-vm.sourceforge.net/#[[Arduino%20IDE]])

          If, like you did, deploying manually, you have to know exactly what to do. You have to know that arduino.exe is called like this:

          cd ../../..
          ./arduino.exe --upload 'libraries/HaikuVM/ArduinoSketch/HaikuVM/HaikuVM.ino'
          

          (as defined in entry arduinoIDEUpload.Upload in file HaikuVM.properties.)

          Therefore, putting arduino.exe into your path has no effect. All depends on "cd C:\arduino-1.0.5\libraries" (or similar).

          [OT]
          No, you are wrong: 0x80000000L and 0x80000000 are not similar if using with Java long.
          To see this, please run (with regular Java or HaikuVM 32/64): https://sourceforge.net/p/haiku-vm/code/HEAD/tree/trunk/gallerie/src/main/java/avr/gallerie/user/TimReset/LongTest.java
          Your are only right for the tiny non-standard micro cosmos of HaikuVM 16/32. In general I recommend to write
          0b10000000000000000000000000000000L instead of
          0b10000000000000000000000000000000.

           

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.