Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support RC receivers #1071

Closed
Resseguie opened this issue Mar 27, 2016 · 56 comments
Closed

Support RC receivers #1071

Resseguie opened this issue Mar 27, 2016 · 56 comments

Comments

@Resseguie
Copy link
Collaborator

I'd like to add a class to support traditional hobby RC receivers to control nodebots. I happen to have a Spektrum radio and compatible receivers, but other popular Tx/Rx (transmitter/receiver - common abbreviations in RC world) combinations should be supported, too.

Details of my exact setup:
DX6 Radio: http://amzn.to/1Se2hjd
AR610 Rx: http://amzn.to/1UQfqVw
(There are Spektrum compatible Tx an Rx made by Orange that are much cheaper.)

It should support individual channels (like the AR610), but also the PPM (Pulse Position Modulation) varieties.

There are a number of examples of using these with Arduino that could get us started.
https://www.sparkfun.com/tutorials/348
http://rcarduino.blogspot.com/2012/01/how-to-read-rc-receiver-with.html

How generic should such a component be?

@rwaldron
Copy link
Owner

I've been casually thinking about this for about a year and the basic sketch I always return to is:

  • Receiver class
    • channels property, that's an array and the values are the most recently received value
  • Transmitter class
    • ?

That's about as far as I usually get. It's critically important that these are designed with the controller pattern.

@Resseguie
Copy link
Collaborator Author

I'm not sure we need Transmitter? Seems like that's on "the other end" that we don't care about. At least I can't think of a use case. We just need to know the value the transmitter sends, via the receiver.

I'll need to think through the channels property, to make sure that makes sense for PPM as well. (I think so, but want to think it through.)

@rwaldron
Copy link
Owner

That may very well be the case, I just figured I'd put it there and we can let the junk shake out :)

@rwaldron
Copy link
Owner

(There are Spektrum compatible Tx an Rx made by Orange that are much cheaper.)

I just purchased these, should receive them tomorrow

@rwaldron
Copy link
Owner

The major issue we face is that pulseIn is process blocking. Some things to consider:

I like the idea of defining our own I2C backpacked peripheral, because we can control every aspect. I've come to realize that the design decisions made by many (not all) hardware creators is generally not very compatible with our async goals.

@henricavalcante
Copy link
Contributor

Is there a way to handle PPM signal over firmata?

@rwaldron
Copy link
Owner

@henricavalcante no, but it can be made available via I2C

@rwaldron
Copy link
Owner

@Resseguie

I assembled this gizmo as a basic "backpack" starting point. In the photos below, you'll notice that the intention is to plug this thing directly into the receiver unit's channel pins. The fritzing shows everything facing forward, but that's just only for illustrative purposes (and a limitation of fritzing)






G: Ground
V: VCC
C: Channel

I haven't added a label for channels, because my actual receiver won't arrive until later today, so I'm not sure which direction the channels are in.

@Resseguie
Copy link
Collaborator Author

Nice. I'll try to build something similar.

@rwaldron
Copy link
Owner

Welllllllll my receiver just arrived and unfortunately the one I ordered has notches

@rwaldron
Copy link
Owner

rwaldron commented Apr 1, 2016

I cut them out and made it fit. Then I made a newer, less messy backpack. Pictures later

@Resseguie
Copy link
Collaborator Author

That's the same Rx I have. FYI, the typical method for connecting such a receiver to your RC project (drone, truck, etc) is with a breakout cable, such as this Naze32 one: http://www.readymaderc.com/store/index.php?main_page=product_info&products_id=3062

That also gives you more flexibility in positioning components on your build. I'll probably make my backpack to accept something similar to that.

Note that weight is also a factor for many uses of this, so I'd like to investigate the minimum backpack necessary once we get the design working.

@rwaldron
Copy link
Owner

rwaldron commented Apr 4, 2016

The second one I built significantly reduced materials:







FYI, the typical method for connecting such a receiver to your RC project (drone, truck, etc) is with a breakout cable, such as this Naze32 one: http://www.readymaderc.com/store/index.php?main_page=product_info&products_id=3062
That also gives you more flexibility in positioning components on your build. I'll probably make my backpack to accept something similar to that.

But you won't be able to use such a thing if you're trying to read the pulse values from the receiver—right? You're effectively replacing this thing and adding a "middle man" to intercept the pulses to do something else with them.

@henricavalcante
Copy link
Contributor

I'm testing the Pin Change Interrupts to decode pwm signal and it works, but I'm trying to figure out a way to send the information to johnny-five like firmata 'cause I'm using a crius board which uses atmega 2560 and has more sensors and I need them, when I upload firmata to board I cant read pwm signals, when I upload my code with Pin Change Interrupt I only access pwm signals from receiver.

This is the board:
crius-layout

This is the receiver:
turnigy-9x-reciever-9x8cv2

I have a quadcopter working well with this firmware and I'm trying to make a car controlled by johnny-five.

There is a way to rewrite firmata with Pin Change Interrupts in some pins?
I want still firmata and read pwm signals in same board.

something like:

  • Rc Receiver send pwm signal to arduino pin,
  • Arduino pin receive pwm signal by interruptions
  • There is a function in 'RCFirmata' to mensure and convert pwm to percentage
  • RCFirmata send's It by serial to johnny-five

I'm crazy or there is a way to make it possible?

@rwaldron
Copy link
Owner

rwaldron commented Apr 5, 2016

The reader code will go on a different microprocessor. I have some super basic firmware written that gets us 6 channels of PPM values. How are you using the pin change interrupts? I couldn't get them to do what I wanted. Can you share your work so far? Tomorrow I will push out what I have so far

@henricavalcante
Copy link
Contributor

It's my setup:
img_20160405_245759492
img_20160405_245810435
img_20160405_245818821

It's my code:

#include <PinChangeInt.h> //https://github.com/GreyGnome/PinChangeInt

#define FIRST_PIN 62

volatile int pwm[] = {0,0,0,0,0,0,0,0,0,0};
volatile int last_up[] = {0,0,0,0,0,0,0,0,0,0};

void up()
{
  uint8_t pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(pin, &down, FALLING);
  last_up[pin - FIRST_PIN] = micros();
}

void down() {
  uint8_t pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(pin, &up, RISING);
  pwm[pin - FIRST_PIN] = micros()-last_up[pin - FIRST_PIN];
  Serial.print(pin);
  Serial.print(" - ");
  Serial.println(pwm[pin - FIRST_PIN]);
}

void setup() {
  pinMode(A8, INPUT); digitalWrite(A8, HIGH);
  pinMode(A9, INPUT); digitalWrite(A9, HIGH);
  pinMode(A10, INPUT); digitalWrite(A10, HIGH);
  pinMode(A11, INPUT); digitalWrite(A11, HIGH);
  pinMode(A12, INPUT); digitalWrite(A12, HIGH);
  pinMode(A13, INPUT); digitalWrite(A13, HIGH);
  pinMode(A14, INPUT); digitalWrite(A14, HIGH);
  pinMode(A15, INPUT); digitalWrite(A15, HIGH);
  Serial.begin(115200);

  //start interrupts
  PCintPort::attachInterrupt(A8, &up, RISING);
  PCintPort::attachInterrupt(A9, &up, RISING);
  PCintPort::attachInterrupt(A10, &up, RISING);
  PCintPort::attachInterrupt(A11, &up, RISING);
  PCintPort::attachInterrupt(A12, &up, RISING);
  PCintPort::attachInterrupt(A13, &up, RISING);
  PCintPort::attachInterrupt(A14, &up, RISING);
  PCintPort::attachInterrupt(A15, &up, RISING);
}

void loop() { }

And this is some serial output:

66 - 1456
62 - 1472
64 - 680
65 - 1156
63 - 1476
68 - 1464
67 - 1084
66 - 1448
62 - 1476
64 - 684
65 - 1156
63 - 1480
68 - 1468
67 - 1080
66 - 1452
62 - 1476
64 - 680
65 - 1156
63 - 1476
68 - 1464
67 - 1080
66 - 1452
62 - 1472
64 - 680
65 - 1156
63 - 1476
68 - 1464

When I move the sticks on controller, values on serial output change between ~600 and ~1500

@henricavalcante
Copy link
Contributor

Are you getting receiver PPM signal by channels output? how?
If you get signal from each channel should be a PWM signal. Have you checked it on oscilloscope?

@rwaldron
Copy link
Owner

rwaldron commented Apr 5, 2016

If you get signal from each channel should be a PWM signal

Yes, I was mistaken.

Here is the crude firmware I wrote:

#include <Wire.h>

#define DEBUG_MODE 1

// Address Pins
#define AD0 8
#define AD1 9


// I2C Defaults
#define I2C_DEFAULT_ADDRESS 0x0A
#define I2C_BUFFER_SIZE 12

byte buffer[I2C_BUFFER_SIZE];

int addressPins[] = { AD0, AD1 };
int address = I2C_DEFAULT_ADDRESS;
int first = 2;
int channels = 6;

void resetState() {
  for (int i = first; i < first + channels; i++) {
    pinMode(i, INPUT);
  }
}

void setup() {

  int offset = 0;

  for (int i = 0; i < 2; i++) {
    pinMode(addressPins[i], INPUT);
    if (digitalRead(addressPins[i])) {
      offset |= 1 << i;
    }
  }

  address += offset;

  #if DEBUG_MODE
    Serial.begin(9600);
  #endif

  resetState();

  Wire.begin(address);
  Wire.onRequest(onRequest);
}

void loop() {

  uint16_t pulses[channels];

  for (int i = first; i < first + channels; i++) {
    pulses[i - first] = pulseIn(i, HIGH, 35000);
  }

  #if DEBUG_MODE
    for (int i = 0; i < channels; i++) {
      Serial.print(i);
      Serial.print(": ");
      Serial.println(pulses[i]);
    }
  #endif

  buffer[0] = pulses[0] >> 8;
  buffer[1] = pulses[0] & 0xFF;
  buffer[2] = pulses[1] >> 8;
  buffer[3] = pulses[1] & 0xFF;
  buffer[4] = pulses[2] >> 8;
  buffer[5] = pulses[2] & 0xFF;
  buffer[6] = pulses[3] >> 8;
  buffer[7] = pulses[3] & 0xFF;
  buffer[8] = pulses[4] >> 8;
  buffer[9] = pulses[4] & 0xFF;
  buffer[10] = pulses[5] >> 8;
  buffer[11] = pulses[5] & 0xFF;
}

void onRequest() {
  Wire.write(buffer, I2C_BUFFER_SIZE);
}

I don't want to put this inside any version of Firmata, because that means it's not available on any other platform that Johnny-Five supports. The benefit of using the I2C slave backpack approach is that all we need is I2C support on other platforms and we're good to go. I'm going to try to combine your interrupt approach with my slave firmware (I will obviously cite you as co-author)

@rwaldron
Copy link
Owner

rwaldron commented Apr 5, 2016

I'm not sure we should use this: https://github.com/GreyGnome/PinChangeInt it's not available via the Arduino IDE "Manage Libraries" interface, which means people have to go through the hassle of adding it via the "Add .ZIP Library". Both are terrible.

Also, there is a notice on the repo:

NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
This library is deprecated as of April 3, 2015. This means that I will be
providing bug fixes for some time, but users are encouraged to migrate to
the EnableInterrupt library, at https://github.com/GreyGnome/EnableInterrupt .
It is faster and easier to use. Thank you.

The version referenced does exist in the Arduino IDE's Library Manager.

@rwaldron
Copy link
Owner

rwaldron commented Apr 5, 2016

The interrupt version is giving me wildly inaccurate results and appears to be slower than the version with pulseIn... which is strange.

@henricavalcante
Copy link
Contributor

What if we use EnableInterrupt instead?

@rwaldron
Copy link
Owner

rwaldron commented Apr 5, 2016

We'll have to either way.

@rwaldron
Copy link
Owner

rwaldron commented Apr 5, 2016

So... this works awesome when channels is set to 1. Once it I set back to 6 or 8, the values are garbage.

#define EI_ARDUINO_INTERRUPTED_PIN
#include <EnableInterrupt.h>
#include <Wire.h>

#define DEBUG_MODE 1

// Address Pins
#define AD0 11
#define AD1 12

// I2C Defaults
#define I2C_DEFAULT_ADDRESS 0x0A
#define I2C_BUFFER_SIZE 16

byte buffer[I2C_BUFFER_SIZE];

int addressPins[] = { AD0, AD1 };
int address = I2C_DEFAULT_ADDRESS;
int first = 2;
int channels = 1;

volatile int pulses[8];
volatile int last_up[8];

void up() {
  uint8_t pin = arduinoInterruptedPin;
  enableInterrupt(pin, &down, FALLING);

  #if DEBUG_MODE
    Serial.print("pin up: ");
    Serial.println(pin);
  #endif

  last_up[pin - first] = micros();
}

void down() {
  uint8_t pin = arduinoInterruptedPin;
  enableInterrupt(pin, &up, RISING);

//  #if DEBUG_MODE
//    Serial.print("pin down: ");
//    Serial.println(pin);
//  #endif

  pulses[pin - first] = micros() - last_up[pin - first];

  #if DEBUG_MODE
    Serial.print(pin);
    Serial.print(": ");
    Serial.println(pulses[pin - first]);
  #endif
}

void resetState() {
  for (int i = first; i < first + channels; i++) {
    pulses[i - first] = 0;
    last_up[i - first] = 0;

    pinMode(i, INPUT_PULLUP);
    enableInterrupt(i, &up, RISING);
  }
}

void setup() {

  // First check if the address was set via pins 11 or 12
  int offset = 0;

  for (int i = 0; i < 2; i++) {
    pinMode(addressPins[i], INPUT);
    if (digitalRead(addressPins[i])) {
      offset |= 1 << i;
    }
  }

  address += offset;


  resetState();

  #if DEBUG_MODE
    Serial.begin(9600);
  #endif


  // Initialize Slave
  Wire.begin(address);
  Wire.onRequest(onRequest);
  Wire.onReceive(onReceive);
}

void loop() {
  for (int i = 0; i < channels; i++) {
    buffer[i * 2] = pulses[i] >> 8;
    buffer[i * 2 + 1] = pulses[i] & 0xFF;
  }  
}

void onRequest() {
  Wire.write(buffer, I2C_BUFFER_SIZE);
}

void onReceive(int count) {
  while (Wire.available()) {
    // Command 0x01 => Reset. 
    if (Wire.read() == 0x01) {
      Serial.println("RESET");
      resetState();  
    }
  }
}

............

Can we discuss the pros and cons of pulseIn vs interrupts, given the context (which is: 100% dedicated microprocessor that must only write to I2C bus)

@henricavalcante
Copy link
Contributor

Im going home now and I will make some tests about differences, interrupts should be better because is non blocking, but I'm thinking, if you want to make a hardware exclusive to convert signals why not convert pwm to analog signal with capacitors and use arduino ADC ports?

@henricavalcante
Copy link
Contributor

Are you building a tessel 2 module?

@henricavalcante
Copy link
Contributor

I'm getting better results using atmega 2560 microprocessor than using atmega 328p, trying to figure out the problem.
img_20160405_201640392

@henricavalcante
Copy link
Contributor

Check using without Serial output inside interrupt delegates like this:

#define EI_ARDUINO_INTERRUPTED_PIN
#include <EnableInterrupt.h>
#include <Wire.h>

#define DEBUG_MODE 1

// Address Pins
#define AD0 11
#define AD1 12

// I2C Defaults
#define I2C_DEFAULT_ADDRESS 0x0A
#define I2C_BUFFER_SIZE 16

byte buffer[I2C_BUFFER_SIZE];

int addressPins[] = { AD0, AD1 };
int address = I2C_DEFAULT_ADDRESS;
int first = 2;
int channels = 8;

volatile int pulses[8];
volatile int last_up[8];

void up() {
  volatile uint8_t pin = arduinoInterruptedPin;
  enableInterrupt(pin, &down, FALLING);
  last_up[pin - first] = micros(); 
}

void down() {
  volatile uint8_t pin = arduinoInterruptedPin;
  enableInterrupt(pin, &up, RISING);
  pulses[pin - first] = micros() - last_up[pin - first];
}

void resetState() {
  for (int i = first; i < first + channels; i++) {
    pulses[i - first] = 0;
    last_up[i - first] = 0;

    pinMode(i, INPUT_PULLUP);
    enableInterrupt(i, &up, RISING);
  }
}

void setup() {

  // First check if the address was set via pins 11 or 12
  int offset = 0;

  for (int i = 0; i < 2; i++) {
    pinMode(addressPins[i], INPUT);
    if (digitalRead(addressPins[i])) {
      offset |= 1 << i;
    }
  }

  address += offset;

  resetState();

  #if DEBUG_MODE
    Serial.begin(9600);
  #endif

  // Initialize Slave
  Wire.begin(address);
  Wire.onRequest(onRequest);
  Wire.onReceive(onReceive);
}

void loop() {
  #if DEBUG_MODE
    for (int i = first; i < first + channels - 1; i++) {
      Serial.print(pulses[i - first]);
      Serial.print("  - ");
    }
    Serial.println(pulses[channels - 1]);
  #endif
  for (int i = 0; i < channels; i++) {
    buffer[i * 2] = pulses[i] >> 8;
    buffer[i * 2 + 1] = pulses[i] & 0xFF;
  } 
}

void onRequest() {
  Wire.write(buffer, I2C_BUFFER_SIZE);
}

void onReceive(int count) {
  while (Wire.available()) {
    // Command 0x01 => Reset. 
    if (Wire.read() == 0x01) {
      Serial.println("RESET");
      resetState();  
    }
  }
}

works like a charm here with arduino pro mini (atmega 328p 16MHZ) ;)

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

Are you building a tessel 2 module?

Not specifically, but I plan to use this with Tessel 2 :D

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

@henricavalcante 👏 this works perfectly

@soundanalogous
Copy link

I guess I didn't realize that Serial output inside the interrupt would cause such issues

Yeah typically you want to do as little as possible inside the interrupt service handler (ISR) - usually just toggle a variable (marked as "volatile") and then based on the state of that variable, do any actual reporting or heavy calculations in the main loop. Some architectures limit the size of the stack for the ISR to just a few bytes so you can't do much and really want to return as quickly as possible.

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

Some architectures limit the size of the stack for the ISR to just a few bytes so you can't do much and really want to return as quickly as possible.

I feel like I learned a lot today—thanks for providing that additional insight :)

@soundanalogous
Copy link

There are also scenarios where you want to disable the interrupts globally while executing a piece of code. You have to approach from the perspective of what happens if an interrupt is triggered while some block of code is executing and if so disable interrupts globally before that block and reenable after that block. You will see a lot of this if you dig into the code in Adafruit libraries and other well-written Arduino libraries that use interrupts. You'll see this in PJRC libs as well.

@soundanalogous
Copy link

here's an example where disabling and reenabling global interrupts is useful: https://github.com/PaulStoffregen/CapacitiveSensor/blob/master/CapacitiveSensor.cpp#L154-L183

@fivdi
Copy link
Contributor

fivdi commented Apr 6, 2016

One scenario where it's necessary to disable interrupts in the above code is when the pulses array is accessed in the loop function.

void loop() {
  for (int i = 0; i < channels; i++) {
    buffer[i * 2] = pulses[i] >> 8;
    buffer[i * 2 + 1] = pulses[i] & 0xFF;
  }  
}

When the following code is executed

    buffer[i * 2] = pulses[i] >> 8;

there is no guarantee that loading the values of the two bytes of the 16 bit integer pulses[i] into registers is an atomic operation that can't be interrupted. There may be an interrupt after the first byte is loaded and before the second byte is loaded. This can potentially mess things things up.

The same applies to the following code

    buffer[i * 2 + 1] = pulses[i] & 0xFF;

There is a similar issue with the following code

    buffer[i * 2] = pulses[i] >> 8;
    buffer[i * 2 + 1] = pulses[i] & 0xFF;

There may be an interrupt between the first and second line of code that changes the value of pulses[i].

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

@fivdi

This should address that?

int pulse = pulses[i];

buffer[i * 2] = pulse >> 8;
buffer[i * 2 + 1] = pulse & 0xFF;

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

@soundanalogous

Per your suggestion, I tried using the noInterrupt()/interrupt() calls (in a few different places), but everywhere I tried just crashed the board :\

@fivdi
Copy link
Contributor

fivdi commented Apr 6, 2016

This should address that?

int pulse = pulses[i];

buffer[i * 2] = pulse >> 8;
buffer[i * 2 + 1] = pulse & 0xFF;

@rwaldron

I'm afraid not. The AVR 8-bit instructions set doesn't have an instruction for loading 16 bit values. This forces to the compiler to generate two instructions each of which loads a single byte. Because there are two instructions there can be an interrupt between the execution of the first and second.

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

Well, then I guess it's a deal breaker.

Can someone tell me why we need to use interrupts here?

@henricavalcante
Copy link
Contributor

I think because reading pwm values with pulseIn will block everything, with interrupts the microprocessor will be able to work between pwm edges. I will try to figure out a way to make data atomic while i2c buffering.

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

I think because reading pwm values with pulseIn will block everything, with interrupts the microprocessor will be able to work between pwm edges.

Thanks, I know why we might want to use interrupts in general, but that strategy is generally used in scenarios where the entire program, including all user application code is running on the same processor. We're only using this to read a maximum of 8 channels (maybe someone will want to use a 10 or 12 channel unit, but I'll worry about that when the day actually comes), and write the values to the I2C bus...

@rwaldron rwaldron closed this as completed Apr 6, 2016
@rwaldron rwaldron reopened this Apr 6, 2016
@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

Whoops!

@henricavalcante
Copy link
Contributor

@rwaldron have you tried like this:

for (int i = 0; i < channels; i++) {
    noInterrupts();
    buffer[i * 2] = pulses[i] >> 8;
    buffer[i * 2 + 1] = pulses[i] & 0xFF;
    interrupts();
  } 

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

I thought I did and I thought I saw it crashing. It's not crashing now.

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

I'm signing off for a bit.

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

Presently, this is not crashing:

#define EI_ARDUINO_INTERRUPTED_PIN
#include <EnableInterrupt.h>
#include <Wire.h>

#define DEBUG_MODE 1

// Address Pins
#define AD0 11
#define AD1 12

// I2C Defaults
#define I2C_DEFAULT_ADDRESS 0x0A
#define I2C_BUFFER_SIZE 16

byte buffer[I2C_BUFFER_SIZE];

int addressPins[] = { AD0, AD1 };
int address = I2C_DEFAULT_ADDRESS;
int first = 2;
int channels = 8;

volatile int pulses[8];
volatile int last_up[8];

void up() {
  volatile uint8_t pin = arduinoInterruptedPin;
  enableInterrupt(pin, &down, FALLING);
  last_up[pin - first] = micros();
}

void down() {
  volatile uint8_t pin = arduinoInterruptedPin;
  enableInterrupt(pin, &up, RISING);
  pulses[pin - first] = micros() - last_up[pin - first];
}

void resetState() {
  for (int i = first; i < first + channels; i++) {
    pulses[i - first] = 0;
    last_up[i - first] = 0;

    pinMode(i, INPUT_PULLUP);
    enableInterrupt(i, &up, RISING);
  }
}

void setup() {

  // First check if the address was set via pins 11 or 12
  int offset = 0;

  for (int i = 0; i < 2; i++) {
    pinMode(addressPins[i], INPUT);
    if (digitalRead(addressPins[i])) {
      offset |= 1 << i;
    }
  }

  address += offset;

  resetState();

  #if DEBUG_MODE
    Serial.begin(9600);
  #endif

  // Initialize Slave
  Wire.begin(address);
  Wire.onRequest(onRequest);
  Wire.onReceive(onReceive);
}

void loop() {
  #if DEBUG_MODE
    for (int i = first; i < first + channels - 1; i++) {
      Serial.print(pulses[i - first]);
      Serial.print("  - ");
    }
    Serial.println(pulses[channels - 1]);
  #endif
  for (int i = 0; i < channels; i++) {
    noInterrupts();
    buffer[i * 2] = pulses[i] >> 8;
    buffer[i * 2 + 1] = pulses[i] & 0xFF;
    interrupts();
  }
}

void onRequest() {
  Wire.write(buffer, I2C_BUFFER_SIZE);
}

void onReceive(int count) {
  while (Wire.available()) {
    // Command 0x01 => Reset.
    if (Wire.read() == 0x01) {
      Serial.println("RESET");
      resetState();
    }
  }
}

@henricavalcante
Copy link
Contributor

It's not crashing here too, which node code are you using in tessel 2 to get values from I2C?

@rwaldron
Copy link
Owner

rwaldron commented Apr 6, 2016

I've been working on a plugin component Receiver class all day, I will push it out tomorrow. I have to leave my computer for the rest of the day.

@henricavalcante
Copy link
Contributor

ok I'll wait until tomorrow ;)
I was trying something like this without success:

const five = require('johnny-five');
const board = new five.Board();

board.on('ready', function(){
  const opts = {
    address: 0x0A
  };

  this.io.i2cConfig(opts);

  this.io.i2cRead(opts.address, 16, function(data) {
    console.log(data);
  })
});

@Resseguie
Copy link
Collaborator Author

Looks like I was away for a few days and missed some fun discussion.

Regarding that Naze cable:

But you won't be able to use such a thing if you're trying to read the pulse values from the receiver—right? You're effectively replacing this thing and adding a "middle man" to intercept the pulses to do something else with them.

I really only meant that I'll probably have male pins on my backpack so I could use such a cable since you can get something similar for most receivers. Then you don't have to worry about having female headers shaped a specific way to fit a particular receiver onto a backpack.

@rwaldron
Copy link
Owner

rwaldron commented Apr 8, 2016

@Resseguie got it!

@rwaldron
Copy link
Owner

rwaldron commented Apr 8, 2016

@henricavalcante @Resseguie here's what I've got so far... https://github.com/rwaldron/j5-rc-receiver

@henricavalcante
Copy link
Contributor

I'll test asap. btw great work. =)

@rwaldron
Copy link
Owner

rwaldron commented Apr 9, 2016

Thanks! It's pretty fun :D

@dtex
Copy link
Collaborator

dtex commented Jan 8, 2018

This just landed in configurableFirmata https://github.com/git-developer/RCSwitchFirmata Seemed relevant.

@dtex
Copy link
Collaborator

dtex commented Jan 11, 2018

Hi @Resseguie,

I'm working on cleaning up the J5 issues list (it was getting kind of hairy). I've identified a subset of issues that are requests for new classes, devices or features and have added them to an index of requested features. Hopefully it will be a place where motivated contributors can find cool things to work on.

This request is listed under the new "Receiver" class.

@dtex dtex closed this as completed Jan 11, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants