DMX output and PING sensor input won't work together

Hi there!

For an art installation, I would like to read data from 3 Parallax ultrasonic PING distance sensors. These values will be used in my code to feed a function that detects if someone walks through a sensor line. In the end, depending on which sensor a distance change is detected, I would like to feed different brightness values into a DMX dimmer. That’s it.

I have all the hardware, built the DMX inverter, to. I got DMX working with DmxSimple.h, and also the 3 PING sensors with NewPing.h.
Problem: as soon as I want to use them both at the same time in one program, either the DMX output doesn’t work (doesn’t change the brightness value on the output), or the sensors are not read (or just one of them).

I assume that they both use the same timer register of the Arduino, but as I’m new in C++ and in Arduino, I’m totally lost now in regards how to get out of this dilemma…

Here a simple example code where the problem already occurs. It would be too complicated to post my whole code, including the algorithms for the triggerings. It’s not needed, I think. The code sample here not much more than two Arduino code examples taken from the web, and after successfully testing them separately, being merged into one program.

#include <DmxSimple.h>
#include <NewPing.h>

#define SONAR_NUM     3  // Number or sensors.
#define MAX_DISTANCE 400 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 30 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(22, 22, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(24, 24, MAX_DISTANCE),
  NewPing(26, 26, MAX_DISTANCE)  
};

unsigned long lasttime = 0;
int brightness = 0;

void setup() {
  Serial.begin(9600); 

  DmxSimple.usePin(11);        //Yes, my DMX hardware is connected to Pin11. I tested it in a separate code: it worked!
  DmxSimple.maxChannel(4);
  
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;

}

void loop() {
  
  long currenttime = millis();  
  if(lasttime - currenttime > 10) {
    lasttime = currenttime;
    dmxloop();
  }
  
  for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through all the sensors.
    if (millis() >= pingTimer[i]) {         // Is it this sensor's time to ping?
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  // Set next time this sensor will be pinged.
      if (i == 0 && currentSensor == SONAR_NUM - 1) oneSensorCycle(); // Sensor ping cycle complete, do something with the results.
      sonar[currentSensor].timer_stop();          // Make sure previous timer is canceled before starting a new ping (insurance).
      currentSensor = i;                          // Sensor being accessed.
      cm[currentSensor] = 0;                      // Make distance zero in case there's no ping echo for this sensor.
      sonar[currentSensor].ping_timer(echoCheck); // Do the ping (processing continues, interrupt will call echoCheck to look for echo).
    }
  }

 
}

void echoCheck() { // If ping received, set the sensor distance to array.
  if (sonar[currentSensor].check_timer()) {
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
  }
}

void oneSensorCycle() { // Sensor ping cycle complete, do something with the results.
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    Serial.print(i);
    Serial.print("=");
    Serial.print(cm[i]);
    Serial.print("cm ");
  }
  Serial.println();
}

void dmxloop() {
  Serial.println(brightness);
  brightness++;  
  DmxSimple.write(1, brightness);      
}

Could anyone please help me? The project was fun so far, but being stuck kind of stresses me out. Please…

Greetz,

K

P.S.: Here the first library used in the code:

<DmxSimple.cpp>

/**
 * DmxSimple - A simple interface to DMX.
 *
 * Copyright (c) 2008-2009 Peter Knight, Tinker.it! All rights reserved.
 */
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "pins_arduino.h"

#include "Arduino.h" 
#include "DmxSimple.h"

/** dmxBuffer contains a software copy of all the DMX channels.
  */
volatile uint8_t dmxBuffer[DMX_SIZE];
static uint16_t dmxMax = 16; /* Default to sending the first 16 channels */
static uint8_t dmxStarted = 0;
static uint16_t dmxState = 0;

static volatile uint8_t *dmxPort;
static uint8_t dmxBit = 0;
static uint8_t dmxPin = 3; // Defaults to output on pin 3 to support Tinker.it! DMX shield

void dmxBegin();
void dmxEnd();
void dmxSendByte(volatile uint8_t);
void dmxWrite(int,uint8_t);
void dmxMaxChannel(int);

/* TIMER2 has a different register mapping on the ATmega8.
 * The modern chips (168, 328P, 1280) use identical mappings.
 */
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define TIMER2_INTERRUPT_ENABLE() TIMSK2 |= _BV(TOIE2)
#define TIMER2_INTERRUPT_DISABLE() TIMSK2 &= ~_BV(TOIE2)
#elif defined(__AVR_ATmega8__)
#define TIMER2_INTERRUPT_ENABLE() TIMSK |= _BV(TOIE2)
#define TIMER2_INTERRUPT_DISABLE() TIMSK &= ~_BV(TOIE2)
#else
#define TIMER2_INTERRUPT_ENABLE()
#define TIMER2_INTERRUPT_DISABLE()
/* Produce an appropriate message to aid error reporting on nonstandard
 * platforms such as Teensy.
 */
#warning "DmxSimple does not support this CPU"
#endif


/** Initialise the DMX engine
 */
void dmxBegin()
{
  dmxStarted = 1;

  // Set up port pointers for interrupt routine
  dmxPort = portOutputRegister(digitalPinToPort(dmxPin));
  dmxBit = digitalPinToBitMask(dmxPin);

  // Set DMX pin to output
  pinMode(dmxPin,OUTPUT);

  // Initialise DMX frame interrupt
  //
  // Presume Arduino has already set Timer2 to 64 prescaler,
  // Phase correct PWM mode
  // So the overflow triggers every 64*510 clock cycles
  // Which is 510 DMX bit periods at 16MHz,
  //          255 DMX bit periods at 8MHz,
  //          637 DMX bit periods at 20MHz
  TIMER2_INTERRUPT_ENABLE();
}

/** Stop the DMX engine
 * Turns off the DMX interrupt routine
 */
void dmxEnd()
{
  TIMER2_INTERRUPT_DISABLE();
  dmxStarted = 0;
  dmxMax = 0;
}

/** Transmit a complete DMX byte
 * We have no serial port for DMX, so everything is timed using an exact
 * number of instruction cycles.
 *
 * Really suggest you don't touch this function.
 */
void dmxSendByte(volatile uint8_t value)
{
  uint8_t bitCount, delCount;
  __asm__ volatile (
    "cli\n"
    "ld __tmp_reg__,%a[dmxPort]\n"
    "and __tmp_reg__,%[outMask]\n"
    "st %a[dmxPort],__tmp_reg__\n"
    "ldi %[bitCount],11\n" // 11 bit intervals per transmitted byte
    "rjmp bitLoop%=\n"     // Delay 2 clock cycles. 
  "bitLoop%=:\n"\
    "ldi %[delCount],%[delCountVal]\n"
  "delLoop%=:\n"
    "nop\n"
    "dec %[delCount]\n"
    "brne delLoop%=\n"
    "ld __tmp_reg__,%a[dmxPort]\n"
    "and __tmp_reg__,%[outMask]\n"
    "sec\n"
    "ror %[value]\n"
    "brcc sendzero%=\n"
    "or __tmp_reg__,%[outBit]\n"
  "sendzero%=:\n"
    "st %a[dmxPort],__tmp_reg__\n"
    "dec %[bitCount]\n"
    "brne bitLoop%=\n"
    "sei\n"
    :
      [bitCount] "=&d" (bitCount),
      [delCount] "=&d" (delCount)
    :
      [dmxPort] "e" (dmxPort),
      [outMask] "r" (~dmxBit),
      [outBit] "r" (dmxBit),
      [delCountVal] "M" (F_CPU/1000000-3),
      [value] "r" (value)
  );
}

/** DmxSimple interrupt routine
 * Transmit a chunk of DMX signal every timer overflow event.
 * 
 * The full DMX transmission takes too long, but some aspects of DMX timing
 * are flexible. This routine chunks the DMX signal, only sending as much as
 * it's time budget will allow.
 *
 * This interrupt routine runs with interrupts enabled most of the time.
 * With extremely heavy interrupt loads, it could conceivably interrupt its
 * own routine, so the TIMER2 interrupt is disabled for the duration of
 * the service routine.
 */
ISR(TIMER2_OVF_vect,ISR_NOBLOCK) {

  // Prevent this interrupt running recursively
  TIMER2_INTERRUPT_DISABLE();

  uint16_t bitsLeft = F_CPU / 31372; // DMX Bit periods per timer tick
  bitsLeft >>=2; // 25% CPU usage
  while (1) {
    if (dmxState == 0) {
      // Next thing to send is reset pulse and start code
      // which takes 35 bit periods
      uint8_t i;
      if (bitsLeft < 35) break;
      bitsLeft-=35;
      *dmxPort &= ~dmxBit;
      for (i=0; i<11; i++) _delay_us(8);
      *dmxPort |= dmxBit;
      _delay_us(8);
      dmxSendByte(0);
    } else {
      // Now send a channel which takes 11 bit periods
      if (bitsLeft < 11) break;
      bitsLeft-=11;
      dmxSendByte(dmxBuffer[dmxState-1]);
    }
    // Successfully completed that stage - move state machine forward
    dmxState++;
    if (dmxState > dmxMax) {
      dmxState = 0; // Send next frame
      break;
    }
  }
  
  // Enable interrupts for the next transmission chunk
  TIMER2_INTERRUPT_ENABLE();
}

void dmxWrite(int channel, uint8_t value) {
  if (!dmxStarted) dmxBegin();
  if ((channel > 0) && (channel <= DMX_SIZE)) {
    if (value<0) value=0;
    if (value>255) value=255;
    dmxMax = max((unsigned)channel, dmxMax);
    dmxBuffer[channel-1] = value;
  }
}

void dmxMaxChannel(int channel) {
  if (channel <=0) {
    // End DMX transmission
    dmxEnd();
    dmxMax = 0;
  } else {
    dmxMax = min(channel, DMX_SIZE);
    if (!dmxStarted) dmxBegin();
  }
}


/* C++ wrapper */


/** Set output pin
 * @param pin Output digital pin to use
 */
void DmxSimpleClass::usePin(uint8_t pin) {
  dmxPin = pin;
  if (dmxStarted && (pin != dmxPin)) {
    dmxEnd();
    dmxBegin();
  }
}

/** Set DMX maximum channel
 * @param channel The highest DMX channel to use
 */
void DmxSimpleClass::maxChannel(int channel) {
  dmxMaxChannel(channel);
}

/** Write to a DMX channel
 * @param address DMX address in the range 1 - 512
 */
void DmxSimpleClass::write(int address, uint8_t value)
{
	dmxWrite(address, value);
}
DmxSimpleClass DmxSimple;

P.P.S.: And here the second library code:

<NewPing.cpp>

// 
// Created by Tim Eckel - teckel@leethost.com
// Copyright 2012 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html
//
// See "NewPing.h" for purpose, syntax, version history, links, and more.
// 

#include "NewPing.h"

NewPing::NewPing(uint8_t trigger_pin, uint8_t echo_pin, int max_cm_distance) {
	_triggerBit = digitalPinToBitMask(trigger_pin); // Get the port register bitmask for the trigger pin.
	_echoBit = digitalPinToBitMask(echo_pin);       // Get the port register bitmask for the echo pin.

	_triggerOutput = portOutputRegister(digitalPinToPort(trigger_pin)); // Get the output port register for the trigger pin.
	_echoInput = portInputRegister(digitalPinToPort(echo_pin));         // Get the input port register for the echo pin.

	_triggerMode = (uint8_t *) portModeRegister(digitalPinToPort(trigger_pin)); // Get the port mode register for the trigger pin.

	_maxEchoTime = min(max_cm_distance, MAX_SENSOR_DISTANCE) * US_ROUNDTRIP_CM + (US_ROUNDTRIP_CM / 2); // Calculate the maximum distance in uS.

#if DISABLE_ONE_PIN == true
	*_triggerMode |= _triggerBit; // Set trigger pin to output.
#endif
}
// 
// Standard ping methods
// 

unsigned int NewPing::ping() {
	if (!ping_trigger()) return NO_ECHO;                // Trigger a ping, if it returns false, return NO_ECHO to the calling function.
	while (*_echoInput & _echoBit)                      // Wait for the ping echo.
		if (micros() > _max_time) return NO_ECHO;       // Stop the loop and return NO_ECHO (false) if we're beyond the set maximum distance.
	return (micros() - (_max_time - _maxEchoTime) - 5); // Calculate ping time, 5uS of overhead.
}
unsigned int NewPing::ping_in() {
	unsigned int echoTime = NewPing::ping();          // Calls the ping method and returns with the ping echo distance in uS.
	return NewPingConvert(echoTime, US_ROUNDTRIP_IN); // Convert uS to inches.
}
unsigned int NewPing::ping_cm() {
	unsigned int echoTime = NewPing::ping();          // Calls the ping method and returns with the ping echo distance in uS.
	return NewPingConvert(echoTime, US_ROUNDTRIP_CM); // Convert uS to centimeters.
}
unsigned int NewPing::ping_median(uint8_t it) {
	unsigned int uS[it], last;
	uint8_t j, i = 0;
	uS[0] = NO_ECHO;
	while (i < it) {
		last = ping();           // Send ping.
		if (last == NO_ECHO) {   // Ping out of range.
			it--;                // Skip, don't include as part of median.
			last = _maxEchoTime; // Adjust "last" variable so delay is correct length.
		} else {                       // Ping in range, include as part of median.
			if (i > 0) {               // Don't start sort till second ping.
				for (j = i; j > 0 && uS[j - 1] < last; j--) // Insertion sort loop.
					uS[j] = uS[j - 1]; // Shift ping array to correct position for sort insertion.
			} else j = 0;              // First ping is starting point for sort.
			uS[j] = last;              // Add last ping to array in sorted position.
			i++;                       // Move to next ping.
		}
		if (i < it) delay(PING_MEDIAN_DELAY - (last >> 10)); // Millisecond delay between pings.
	}
	return (uS[it >> 1]); // Return the ping distance median.
}
// 
// Standard ping method support functions (not called directly)
// 
boolean NewPing::ping_trigger() {
#if DISABLE_ONE_PIN != true
	*_triggerMode |= _triggerBit;    // Set trigger pin to output.
#endif
	*_triggerOutput &= ~_triggerBit; // Set the trigger pin low, should already be low, but this will make sure it is.
	delayMicroseconds(4);            // Wait for pin to go low, testing shows it needs 4uS to work every time.
	*_triggerOutput |= _triggerBit;  // Set trigger pin high, this tells the sensor to send out a ping.
	delayMicroseconds(10);           // Wait long enough for the sensor to realize the trigger pin is high. Sensor specs say to wait 10uS.
	*_triggerOutput &= ~_triggerBit; // Set trigger pin back to low.
#if DISABLE_ONE_PIN != true
	*_triggerMode &= ~_triggerBit;   // Set trigger pin to input (when using one Arduino pin this is technically setting the echo pin to input as both are tied to the same Arduino pin).
#endif

	_max_time =  micros() + MAX_SENSOR_DELAY;                  // Set a timeout for the ping to trigger.
	while (*_echoInput & _echoBit && micros() <= _max_time) {} // Wait for echo pin to clear.
	while (!(*_echoInput & _echoBit))                          // Wait for ping to start.
		if (micros() > _max_time) return false;                // Something went wrong, abort.

	_max_time = micros() + _maxEchoTime; // Ping started, set the timeout.
	return true;                         // Ping started successfully.
}
// 
// Timer interrupt ping methods (won't work with ATmega8 and ATmega128)
// 

void NewPing::ping_timer(void (*userFunc)(void)) {
	if (!ping_trigger()) return;         // Trigger a ping, if it returns false, return without starting the echo timer.
	timer_us(ECHO_TIMER_FREQ, userFunc); // Set ping echo timer check every ECHO_TIMER_FREQ uS.
} 
boolean NewPing::check_timer() {
	if (micros() > _max_time) { // Outside the timeout limit.
		timer_stop();           // Disable timer interrupt
		return false;           // Cancel ping timer.
	}

	if (!(*_echoInput & _echoBit)) { // Ping echo received.
		timer_stop();                // Disable timer interrupt
		ping_result = (micros() - (_max_time - _maxEchoTime) - 13); // Calculate ping time, 13uS of overhead.
		return true;                 // Return ping echo true.
	}

	return false; // Return false because there's no ping echo yet.
}
// 
// Timer2/Timer4 interrupt methods (can be used for non-ultrasonic needs)
// 

// Variables used for timer functions
void (*intFunc)();
void (*intFunc2)();
unsigned long _ms_cnt_reset;
volatile unsigned long _ms_cnt;


void NewPing::timer_us(unsigned int frequency, void (*userFunc)(void)) {
	timer_setup();      // Configure the timer interrupt.
	intFunc = userFunc; // User's function to call when there's a timer event.

#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
	OCR4C = min((frequency>>2) - 1, 255); // Every count is 4uS, so divide by 4 (bitwise shift right 2) subtract one, then make sure we don't go over 255 limit.
	TIMSK4 = (1<<TOIE4);                  // Enable Timer4 interrupt.
#else
	OCR2A = min((frequency>>2) - 1, 255); // Every count is 4uS, so divide by 4 (bitwise shift right 2) subtract one, then make sure we don't go over 255 limit.
	TIMSK2 |= (1<<OCIE2A);                // Enable Timer2 interrupt.
#endif
}


void NewPing::timer_ms(unsigned long frequency, void (*userFunc)(void)) {
	timer_setup();                       // Configure the timer interrupt.
	intFunc = NewPing::timer_ms_cntdwn;  // Timer events are sent here once every ms till user's frequency is reached.
	intFunc2 = userFunc;                 // User's function to call when user's frequency is reached.
	_ms_cnt = _ms_cnt_reset = frequency; // Current ms counter and reset value.

#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
	OCR4C = 249;         // Every count is 4uS, so 1ms = 250 counts - 1.
	TIMSK4 = (1<<TOIE4); // Enable Timer4 interrupt.
#else
	OCR2A = 249;           // Every count is 4uS, so 1ms = 250 counts - 1.
	TIMSK2 |= (1<<OCIE2A); // Enable Timer2 interrupt.
#endif
}


void NewPing::timer_stop() { // Disable timer interrupt.
#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
	TIMSK4 = 0;
#else
	TIMSK2 &= ~(1<<OCIE2A);
#endif
}
// 
// Timer2/Timer4 interrupt method support functions (not called directly)
// 

void NewPing::timer_setup() {
#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
	timer_stop(); // Disable Timer4 interrupt.
	TCCR4A = TCCR4C = TCCR4D = TCCR4E = 0;
	TCCR4B = (1<<CS42) | (1<<CS41) | (1<<CS40) | (1<<PSR4); // Set Timer4 prescaler to 64 (4uS/count, 4uS-1020uS range).
	TIFR4 = (1<<TOV4);
	TCNT4 = 0;    // Reset Timer4 counter.
#else
	timer_stop();           // Disable Timer2 interrupt.
	ASSR &= ~(1<<AS2);      // Set clock, not pin.
	TCCR2A = (1<<WGM21);    // Set Timer2 to CTC mode.
	TCCR2B = (1<<CS22);     // Set Timer2 prescaler to 64 (4uS/count, 4uS-1020uS range).
	TCNT2 = 0;              // Reset Timer2 counter.
#endif
}


void NewPing::timer_ms_cntdwn() {
	if (!_ms_cnt--) {            // Count down till we reach zero.
		intFunc2();              // Scheduled time reached, run the main timer event function.
		_ms_cnt = _ms_cnt_reset; // Reset the ms timer.
	}
}


#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
ISR(TIMER4_OVF_vect) {
#else
ISR(TIMER2_COMPA_vect) {
#endif
	if(intFunc) intFunc(); // If wrapped function is set, call it.
}
// 
// Conversion methods (rounds result to nearest inch or cm).
// 
unsigned int NewPing::convert_in(unsigned int echoTime) {
	return NewPingConvert(echoTime, US_ROUNDTRIP_IN); // Convert uS to inches.
}


unsigned int NewPing::convert_cm(unsigned int echoTime) {
	return NewPingConvert(echoTime, US_ROUNDTRIP_CM); // Convert uS to centimeters.
}

Hi!

I have 3 ultrasonic sensors, using them with NewPing.h (http://playground.arduino.cc/Code/NewPing) library. They work fine! :slight_smile:
Then I have a DMX dimmer, for which I built the inverter hardware to connect it to my Arduino Mega. I used the examples with DmxSimple.h (http://code.google.com/p/tinkerit/wiki/DmxSimple), and they work fine, too! :slight_smile:

Problem: Sensors and DMX output don’t work together in one code. Maybe problem with the timer interrupts? I’m a newbie, and I’d appreciate any help! :~

#include <DmxSimple.h>
#include <NewPing.h>

#define SONAR_NUM     3  // Number or sensors.
#define MAX_DISTANCE 400 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 30 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(22, 22, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(24, 24, MAX_DISTANCE),
  NewPing(26, 26, MAX_DISTANCE)  
};

unsigned long lasttime = 0;
int brightness = 0;

void setup() {
  Serial.begin(9600); 

  DmxSimple.usePin(11);
  DmxSimple.maxChannel(4);
  
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;

}

void loop() {
  
  long currenttime = millis();  
  if(lasttime - currenttime > 10) {
    lasttime = currenttime;
    dmxloop();
  }
  
  for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through all the sensors.
    if (millis() >= pingTimer[i]) {         // Is it this sensor's time to ping?
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  // Set next time this sensor will be pinged.
      if (i == 0 && currentSensor == SONAR_NUM - 1) oneSensorCycle(); // Sensor ping cycle complete, do something with the results.
      sonar[currentSensor].timer_stop();          // Make sure previous timer is canceled before starting a new ping (insurance).
      currentSensor = i;                          // Sensor being accessed.
      cm[currentSensor] = 0;                      // Make distance zero in case there's no ping echo for this sensor.
      sonar[currentSensor].ping_timer(echoCheck); // Do the ping (processing continues, interrupt will call echoCheck to look for echo).
    }
  }

 
}

void echoCheck() { // If ping received, set the sensor distance to array.
  if (sonar[currentSensor].check_timer()) {
    cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM;
  }
}

void oneSensorCycle() { // Sensor ping cycle complete, do something with the results.
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    Serial.print(i);
    Serial.print("=");
    Serial.print(cm[i]);
    Serial.print("cm ");
  }
  Serial.println();
}

void dmxloop() {
  Serial.println(brightness);
  brightness++;  
  DmxSimple.write(1, brightness);      
}

Thanks in advance for any help!

Kilian

These topics seemed very similar, so I merged them

The comments in the code for the two libraries strongly suggest that both are using timer 2.

OK, I think I understood that. At least I guessed the nature of the problem to be this.
Now, my big question is - how can I change Timer 2 to, let’s say, Timer 4 in the NewPing library.

Actually, I’ve tried (and “errored”) myself for quite a while and found a way that the NewPing library is now working on Timer4. But I think, there’s something wrong, because the Ping results from the sensor seem to be less reliable than before. It would be nice if you (or someone) could take a look at my modified code in the library “NewPing.cpp”, and tell me if my modifications make sense. I should add that I’m using an Arduino Mega 1280. My modified library has a new name: “NewNewPing”, and so do all the functions and variables that used to be named “NewPing”.

Here I’ll post the part of the “NewNewPing.cpp” code in which I made modifications:

// ---------------------------------------------------------------------------
// Timer interrupt ping methods (won't work with ATmega8 and ATmega128)
// ---------------------------------------------------------------------------

void NewNewPing::ping_timer(void (*userFunc)(void)) {
	if (!ping_trigger()) return;         // Trigger a ping, if it returns false, return without starting the echo timer.
	timer_us(ECHO_TIMER_FREQ, userFunc); // Set ping echo timer check every ECHO_TIMER_FREQ uS.
}

 
boolean NewNewPing::check_timer() {
	if (micros() > _max_time) { // Outside the timeout limit.
		timer_stop();           // Disable timer interrupt
		return false;           // Cancel ping timer.
	}

	if (!(*_echoInput & _echoBit)) { // Ping echo received.
		timer_stop();                // Disable timer interrupt
		ping_result = (micros() - (_max_time - _maxEchoTime) - 13); // Calculate ping time, 13uS of overhead.
		return true;                 // Return ping echo true.
	}

	return false; // Return false because there's no ping echo yet.
}


// ---------------------------------------------------------------------------
// Timer2/Timer4 interrupt methods (can be used for non-ultrasonic needs)
// ---------------------------------------------------------------------------

// Variables used for timer functions
void (*intFunc)();
void (*intFunc2)();
unsigned long _ms_cnt_reset;
volatile unsigned long _ms_cnt;


void NewNewPing::timer_us(unsigned int frequency, void (*userFunc)(void)) {
	timer_setup();      // Configure the timer interrupt.
	intFunc = userFunc; // User's function to call when there's a timer event.

#if defined (__AVR_ATmega32U4__)   // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
	OCR4C = min((frequency>>2) - 1, 255); // Every count is 4uS, so divide by 4 (bitwise shift right 2) subtract one, then make sure we don't go over 255 limit.
	TIMSK4 = (1<<TOIE4);                  // Enable Timer4 interrupt.
#else
	OCR4A = min((frequency>>2) - 1, 255); // Every count is 4uS, so divide by 4 (bitwise shift right 2) subtract one, then make sure we don't go over 255 limit.
	TIMSK4 |= (1<<OCIE4A);                // Enable Timer4 interrupt.
#endif
}


void NewNewPing::timer_ms(unsigned long frequency, void (*userFunc)(void)) {
	timer_setup();                       // Configure the timer interrupt.
	intFunc = NewNewPing::timer_ms_cntdwn;  // Timer events are sent here once every ms till user's frequency is reached.
	intFunc2 = userFunc;                 // User's function to call when user's frequency is reached.
	_ms_cnt = _ms_cnt_reset = frequency; // Current ms counter and reset value.

#if defined (__AVR_ATmega32U4__)  // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
	OCR4C = 249;         // Every count is 4uS, so 1ms = 250 counts - 1.
	TIMSK4 = (1<<TOIE4); // Enable Timer4 interrupt.
#else
	OCR4A = 249;           // Every count is 4uS, so 1ms = 250 counts - 1.
	TIMSK4 |= (1<<OCIE4A); // Enable Timer2 interrupt.
#endif
}


void NewNewPing::timer_stop() { // Disable timer interrupt.
#if defined (__AVR_ATmega32U4__)  // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
	TIMSK4 = 0;
#else
	TIMSK4 &= ~(1<<OCIE4A);
#endif
}


// ---------------------------------------------------------------------------
// Timer2/Timer4 interrupt method support functions (not called directly)
// ---------------------------------------------------------------------------

void NewNewPing::timer_setup() {
#if defined (__AVR_ATmega32U4__)  // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
	timer_stop(); // Disable Timer4 interrupt.
	TCCR4A = TCCR4C = 0;
	TCCR4B = _BV(CS40) | _BV(CS41); // Set Timer4 prescaler to 64 (4uS/count, 4uS-1020uS range).
	TIFR4 = (1<<TOV4);
	TCNT4 = 0;    // Reset Timer4 counter.
#else
	timer_stop();           // Disable Timer4 interrupt.
	ASSR &= ~(1<<AS2);      // Set clock, not pin.
	TCCR4A = (1<<WGM41);    // Set Timer4 to CTC mode.
	TCCR4B = (1<<CS40);     // Set Timer4 prescaler to 64 (4uS/count, 4uS-1020uS range).
	TCNT4 = 0;              // Reset Timer4 counter.
#endif
}


void NewNewPing::timer_ms_cntdwn() {
	if (!_ms_cnt--) {            // Count down till we reach zero.
		intFunc2();              // Scheduled time reached, run the main timer event function.
		_ms_cnt = _ms_cnt_reset; // Reset the ms timer.
	}
}


#if defined (__AVR_ATmega32U4__)  // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
ISR(TIMER4_OVF_vect) {
#else
ISR(TIMER4_COMPA_vect) {
#endif
	if(intFunc) intFunc(); // If wrapped function is set, call it.
}

kilian23:
Hi there!

For an art installation, I would like to read data from 3 Parallax ultrasonic PING distance sensors. These values will be used in my code to feed a function that detects if someone walks through a sensor line. In the end, depending on which sensor a distance change is detected, I would like to feed different brightness values into a DMX dimmer. That's it.

I have all the hardware, built the DMX inverter, to. I got DMX working with DmxSimple.h, and also the 3 PING sensors with NewPing.h.
Problem: as soon as I want to use them both at the same time in one program, either the DMX output doesn't work (doesn't change the brightness value on the output), or the sensors are not read (or just one of them).

I assume that they both use the same timer register of the Arduino, but as I'm new in C++ and in Arduino, I'm totally lost now in regards how to get out of this dilemma...

Here a simple example code where the problem already occurs. It would be too complicated to post my whole code, including the algorithms for the triggerings. It's not needed, I think. The code sample here not much more than two Arduino code examples taken from the web, and after successfully testing them separately, being merged into one program.

Could anyone please help me? The project was fun so far, but being stuck kind of stresses me out. Please.....

I would suggest not using the multiple ping sensor ping_timer() method that you're using. That uses timer 2 and is conflicting with the dimmer library. Instead, change your code to ping the 3 sensors using the standard ping(), ping_in(), or ping_cm() method which won't use timer 2. You'll also need to comment out the code that calls timer 2 in the NewPing.cpp file as shown in this thread:

http://forum.arduino.cc/index.php?topic=126251.0

Tim