NANO PWM Read /write w/serial Tsunami super wav questions

Hello, I am new to code writing. I have made a few nano and trinket projects. Here is current project. I am trying to decode 6 channels of PWM(PPM) input, and then depending on freq of PWM( on off or in between) I plan to trigger an output pin high or low.

I am able to see the PWM input on NANO D2 perfectly. I can trigger some LEDs at "IDLE" and "FULL THROTTLE" (Full throttle in Forward and reverse, hence placed the ends of PWM freq)

Considerations :

  1. identify each input /output channel , only one input is linked to one output
  2. assign an independent function to each output channel (will only be 'on or off' during a specific PWM freq)
  3. monitor each channel in its own column of necessary width to hold "text", min 6 max 10 columns.
  4. Each input may have a spectrum of Freq to look at(1000-2000PWM), or just be at freq limits for basic on-off.

Thoughts:
Do I do this in IF-Else statements or should I change to 'switch case' ?
How to space columns for each channel, in order?
It seems pretty clear that the current code will only allow one input channel 'duration' as this has not been declared appropriately for any other channel than PWM0, adn I do not know how to define it for the other channels.

My current basic code does this perfectly for one channel. It inserts the "txt" followed by the 'duration' just like I want. But I feel that I am going down the wrong path overall.

I provided a jpg of the serial monitor in action as I throttle up the PWM freq.

Thank you in advance for any tips

[code]

// Sketch reads RC PWM (PPM) input pin 2,3,4,5,6,7 and
// can check pulse width millis on serial monitor, maybe output 
// LED to denote certain PWM freq

         // pwm input
int PWM0 = 2;
int PWM1 = 3;
int PWM2 = 4;
int PWM3 = 5; 
int PWM4 = 6;
int PWM5 = 7;

int outhigh = 17; // LED 
int outlow = 15; // LED  
unsigned long duration;


// setup serial and input, output pins
void setup()
{
  Serial.begin(19200);
  pinMode(PWM0, INPUT); // PWM input pin
  pinMode(outhigh, OUTPUT);  //LED pin, lights LED on pin 17
  pinMode(outlow, OUTPUT);// LED pin, lights LED on pin 15
}

void loop(){
{
duration = pulseIn(PWM0, HIGH); 
digitalWrite(outhigh, LOW);
digitalWrite(outlow, LOW);
}

 if (duration > 1050 && duration < 1150)
  {
    digitalWrite(outlow, HIGH);
    Serial.println("FULL THROTTLE");
  }
   
 else if (duration  > 1151 && duration < 1250)
  {
    digitalWrite(outlow, LOW);
    Serial.println("ACELL 3");
  }
 
 else if (duration  > 1251 && duration < 1350)
  {
    digitalWrite(outlow, LOW);
    Serial.println("ACCEL 2");
  }
 
 else if (duration  > 1351 && duration < 1450)
  {
    digitalWrite(outlow, LOW);
    Serial.println("ACCEL 1");    
  }
  
 else if (duration  > 1451 && duration < 1550)
  {
    digitalWrite(outlow, HIGH);
    digitalWrite(outhigh, HIGH);
    Serial.println("IDLE");
  }
  
 else if (duration  > 1551 && duration < 1650)
  {
    digitalWrite(outhigh, LOW);
    Serial.println("ACCEL 1");
  }
  
 else if (duration  > 1651 && duration < 1750)
  {
    digitalWrite(outhigh, LOW);
    Serial.println("ACCEL 2");
  }
 
 else if (duration  > 1751 && duration < 1850)
  {
    digitalWrite(outhigh, LOW);
    Serial.println("ACCEL 3");
  }
  
 else if (duration  > 1851 && duration < 1950)
  {
    digitalWrite(outhigh, HIGH);
    Serial.println("FULL THROTTLE");
  }
 
  Serial.println(duration);
  delay(100); //delay so you can read the scrolling output
}

[/code]

if constructs are more appropriate to check intervals as you do here
A switch statement would work using the ... interval notation of GCC but this is not part of the current C++ standard
So you could go with something like this (not standard)

void setup() {
  Serial.begin(115200);
  int i = 50;
  switch (i) {
    case 0 ... 3:   Serial.println(1); break;
    case 4 ... 10:  Serial.println(2); break;
    case 11 ... 67: Serial.println(3); break;
    default: Serial.println(4);
  }
}

void loop() {}

or this version which is not breaking any standard

void setup() {
  Serial.begin(115200);
  int i = 50;

  if (i >= 0 && i <= 3) {
    Serial.println(1);
  } else if (i >= 4 && i <= 10) {
    Serial.println(2);
  } else if (i >= 11 && i <= 67) {
    Serial.println(3);
  } else
    Serial.println(4);
}

void loop() {}

this is the line that capture the frequency on pin PWM0duration = pulseIn(PWM0, HIGH);The pulseIn() function here waits for the PWM0 pin to go from LOW to HIGH, starts timing, then waits for the pin to go LOW and stops timing and returns (an unsigned long) the length of the pulse in microseconds which you store in duration (or gives up and returns 0 if no complete pulse was received within the timeout)

so you would have to call this function for every pin and then walk through the decision tree. if the intervals and actions are similar, that would be best resolved in a function that you call with the pin number (which would be best declared as a const byte rather than an int) as a parameter


for the interval management, you can proceed the way you do at the moment or put the interval values in an array or a struct that has min, max and a function to call and your code could just be a loop through the intervals so that you don't write tons of if...

as you iterate through the pins, you build up an "answer" message that will take care of the column for example

Hi JML, THX for the tips!! The first code did work fine, but was messy and not what I needed so I appreciate that you steered me away from it. I read through your reply carefully and made some changes. This code I wrote works perfectly and prints the data in columns on the serial monitor(see jpg). It will be expanded to more channels in-out once I figure out the rest of the issues described above. I put the \t before the printed object as it seems cleaner to me. The return is after PWM6 w/delay. Now I have to figure a way to include my pin out instructions without messing this up.

[code]
int PWM1 = 2;
int PWM2 = 3;
int PWM3 = 4;
int PWM4 = 5;
int PWM5 = 6;
int PWM6 = 7;
unsigned long duration;

void setup()
{
  Serial.begin(19200);
  pinMode(PWM1, INPUT);
  pinMode(PWM2, INPUT);
  pinMode(PWM3, INPUT);
  pinMode(PWM4, INPUT);
  pinMode(PWM5, INPUT);
  pinMode(PWM6, INPUT);
}

void loop(){
{
  duration = pulseIn(PWM1, HIGH);
  Serial.print ("PWM1  ");
  Serial.print (duration);
  
}
{
  duration = pulseIn(PWM2, HIGH);
  Serial.print ("\tPWM2  ");
  Serial.print(duration); 
 

} 
{
  duration = pulseIn(PWM3, HIGH);
  Serial.print ("\tPWM3  ");
  Serial.print (duration);
 
}
{
  duration = pulseIn(PWM4, HIGH);
  Serial.print ("\tPWM4  ");
  Serial.print (duration); 
 

} 
{
  duration = pulseIn(PWM5, HIGH);
  Serial.print ("\tPWM5  ");
  Serial.print (duration);
  
}
{
  duration = pulseIn(PWM6, HIGH);
  Serial.print ("\tPWM6  ");
  Serial.println (duration); 
  delay(100);
}
}

[/code]

You don’t need all the {} to separate the blocks

You could use 6 variables

duration1 = pulseIn(PWM1, HIGH);
duration2 = pulseIn(PWM2, HIGH);
duration3 = pulseIn(PWM3, HIGH);
...

or better use an array so that it’s easier to deal with the repetitive nature of your code

const byte PWMPins[] = {2,3,4,5,6,7};
const byte nbPWMPins = sizeof(PWMPins) / sizeof(PWMPins[0]);
unsigned long duration[nbPWMPins];

void setup()
{
   Serial.begin(115200);
   for (byte i=0; i<nbPWMPins; i++) pinMode(PWMPins[i], INPUT);
}

void loop(){
{
   for (byte i=0; i<nbPWMPins; i++) duration[i] = pulseIn(PWMPins[i], HIGH); // read duration for each pins

   // do something with the values
   ...

   // print them out
   for (byte i=0; i<nbPWMPins; i++) {
      Serial.print(F(“PWM”)); Serial.print(i); Serial.print(F(“:”));
      Serial.print(duration[i]);
      if (i != nbPWMPins-1) Serial.print(F(“\t”));
   }

   // do other stuff
   ...
}

to read them all at the beginning of the loop() and then implement your business logic (if they are interdependent then having the 6 might be useful)

Great THX JML, I will try an array next. I am not familiar with how to write it, but will give it a try. I can see how it is much cleaner to code with an array, and I just have to substitute the pins.

Is it any faster to process/run array vs blocks at this level?

Array will be a bit slower since you need to count for the loop and find an index in an array.. but we are talking probably a small few hundreds nanosecond difference there on a UNO and what you gain is easier to write and evolve code. It’s always a trade-off

OK thank you JML. Because I do not know enough yet about how to incorporate arrays, I decided to try to finish building the code block style to get some LEDs blinking and it works for each channel I have tested, and then convert it to an array once I understand it.

I have several delay() of functions in my code (One currently on PWM 5 ). This functions, but delays the program. I have reviewed the 'blink without delay' sketches and I think that this might work for me. The issue is inserting the 'blink without delay' code for x number of LEDs and how to define different 'unsigned long interval = x;' for multiple PIN outs each with different HIGH intervals....

I will spend some time reviewing Robin2's

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

here is current program with delay() on PWM 5. Eventually, I may have to go to a switch case for the pin outs anyway....and down the road, I want to separate the 'Serial.print each channel' from the digitalWrite portions (probably w/an array)

[code]
int PWM1 = 2;
int PWM2 = 3;
int PWM3 = 4;
int PWM4 = 5;
int PWM5 = 6;
int PWM6 = 7;

int WAV1 = 14;
int WAV2 = 15;
int WAV3 = 16;
int WAV4 = 17;
int WAV5 = 18;
int WAV6 = 19;

unsigned long duration;

void setup()
{
  Serial.begin(19200);
  pinMode(PWM1, INPUT);
  pinMode(PWM2, INPUT);
  pinMode(PWM3, INPUT);
  pinMode(PWM4, INPUT);
  pinMode(PWM5, INPUT);
  pinMode(PWM6, INPUT); 

  pinMode(WAV1, OUTPUT);
  pinMode(WAV2, OUTPUT);
  pinMode(WAV3, OUTPUT);
  pinMode(WAV4, OUTPUT);
  pinMode(WAV5, OUTPUT);
  pinMode(WAV6, OUTPUT);  
}

void loop(){
{
  duration = pulseIn(PWM1, HIGH);
  Serial.print ("PWM1  ");
  Serial.print (duration);
  
}
{
  duration = pulseIn(PWM2, HIGH);
  Serial.print ("\tPWM2  ");
   if (duration > 1851 && duration < 1950)
  {
    digitalWrite(WAV2, HIGH);
    Serial.print ("T-Full  ");
  }
   
 else 
  {
  digitalWrite(WAV2, LOW);  
  Serial.print(duration); 
 
} 
}
{
  duration = pulseIn(PWM3, HIGH);
  Serial.print ("\tPWM3  ");
  Serial.print (duration);
 
}
{
  duration = pulseIn(PWM4, HIGH);
  Serial.print ("\tPWM4  ");
  Serial.print (duration); 
 
} 
{
  duration = pulseIn(PWM5, HIGH);
  Serial.print ("\tPWM5  ");
 if (duration >= 1851)
  {
    digitalWrite(WAV5, HIGH);
    delay(100); 
    Serial.print ("CANFIRE");
  }
   
 else 
  {
  digitalWrite(WAV5, LOW);
  Serial.print(duration); 

  }
}
{
  duration = pulseIn(PWM6, HIGH);
  Serial.print ("\tPWM6  ");

   if (duration >= 1851)
  {
    digitalWrite(WAV6, HIGH);
    
    Serial.println ("MGFIRE");
  }
   
 else 
  {
  digitalWrite(WAV6, LOW);
  
  Serial.println (duration); 
  delay(10);
  }
}
}

[/code]

Just curious on why do you need delays for (I suppose that based on the PWM you see the pin needs to stay high or low for a certain time to drive something else) but is it a big problem if you don’t check the other PWM quickly ? how fast does the checking and printing need to be ?

You need indeed to have for each pin a kind of memory of its state and last trigger time. if it’s in the triggered state and the delay has not been reached then you skip reading that one

unsigned long trigger5Time, duration5;
boolean busyFlag5 = false;
const unsigned int delay5 = 200; // 200 ms - an unsigned int is likely large enough for the delays (up to 65535ms = 65 seconds and a half roughly), otherwise use unsigned long

...

if ((busyFlag5 == false) || (millis() - trigger5Time >= delay5)) { // if we are not triggered or if we are triggered and delay has expired 
  duration5 = pulseIn(PWM5, HIGH);
  Serial.print ("\tPWM5  ");
  if (duration5 >= 1851) {
    digitalWrite(WAV5, HIGH); 
    Serial.print ("CANFIRE");
    busyFlag5 = true;
    trigger5Time = millis();
  } else {
    digitalWrite(WAV5, LOW);
    Serial.print(duration5);
   busyFlag5 = false;
  }
}

As you can see there are multiple variables for each PWM pin that will serve as memory. To simplify the code and make it more modular, an array of structures holding each PWM to check and it’s associated variables would make things easier to code.

#include <limits.h> // to define UINT_MAX (65535 on a UNO)

struct channel_T {
  byte pulsePinNumber;
  byte triggerPinNumber;
  unsigned int minThreshold;
  unsigned int maxThreshold;
  boolean busyFlag;
  unsigned int waitingTime;
  unsigned long lasTriggerTime;
  unsigned long pulseDuration;
  const char * message;
};

channel_T channel[] {
  {2, 14, 0, UINT_MAX, false, 10, 0, 0, NULL},
  {3, 15, 1851, 1950 , false, 10, 0, 0, "T-Full"},
  {4, 16, 0, UINT_MAX, false, 10, 0, 0, NULL},
  {5, 17, 1851, UINT_MAX, false, 100, 0, 0, "CANFIRE"},
  {6, 18, 1851, UINT_MAX, false, 10, 0, 0, "MGFIRE"},
  {7, 19, 1300, false, 10, 0, 0, NULL},
};

const byte nbChannels = sizeof(channel) / sizeof(channel[0]);


void setup() {
  Serial.begin(115200);
  for (byte i = 0; i < nbChannels; i++) {
    pinMode(channel[i].pulsePinNumber, INPUT);
    pinMode(channel[i].triggerPinNumber, OUTPUT);
  }
}

void loop() {
  // read pulse duration for each Pin
  for (byte i = 0; i < nbChannels; i++) {
    channel[i].pulseDuration = pulseIn(channel[i].pulsePinNumber, HIGH);
  }

  // do something with the values in a non blocking way
  for (byte i = 0; i < nbChannels; i++) {
    if ((channel[i].busyFlag == false) || (millis() - channel[i].lasTriggerTime >= channel[i].waitingTime)) { // if we are not triggered or if we are triggered and delay has expired
      if ((channel[i].pulseDuration >= channel[i].minThreshold) && (channel[i].pulseDuration <= channel[i].maxThreshold)) {
        digitalWrite(channel[i].triggerPinNumber, HIGH);
        if (channel[i].message) Serial.println(channel[i].message);
        channel[i].busyFlag = true;
        channel[i].lasTriggerTime = millis();
      } else {
        digitalWrite(channel[i].triggerPinNumber, LOW);
        channel[i].busyFlag =  false;
      }
    }
  }

  // print them out
  for (byte i = 0; i < nbChannels; i++) {
    Serial.print(F("PWM")); Serial.print(i + 1); Serial.print(F(":"));
    Serial.print(channel[i].pulseDuration);
    if (channel[i].busyFlag)  Serial.print(F("*"));    // print a star next to the value if channel is active
    if (i != nbChannels - 1) Serial.print(F("\t"));
    else Serial.println();
  }
}

You just iterate over the array in the loop and it will do it all for you. Maybe a bit over your head at the moment but you’ll get there.

Note : code totally untested... so use it to understand the concepts

Thank you JML, this explanation helps. In the first code box, I can kind of see how the code looks at state and time. So rather than running a delay(), this is a kind of side branch timer that will not go back to action until a certain time has been met for a specific object (sorry , don't know all the terms yet).

The second block is more complex and will take me some time to review.

The object of my code is to control a robertsonics tsunami wav board via a 16 channel SBUS radiolink RC Tx Rx. I am currently limited to max 10 channels due to pin configuration on the Nano (which should be enough anyway)

current setup(cumbersome, I know):

  1. Receive sbus signal from RC receiver (Rx)

  2. this signal is split into two pathways. I need both for a project. One goes to a motor control board (which can only decode sbus ) and the other to my sound set-up (the Tsunami). The Rx signal output is either sbus or PWM, not both.

  3. Convert the Rx sbus output into PWM (because I do not know how to parse/time sbus payload yet through the Nano) for my sound card (I do have sbus.h but have not figured it out). So this conversion is currently accomplished with an rmilec unit(works great).

  4. rmilec outputs a clean PWM signal that I can see on the NANO (the code I am working on).

  5. The NANO interprets the PWM code and will then write to a output pin to HIGH or LOW.

  6. The HIGH or LOW will trigger .wav tracks on the TSunami sound card.

  7. Due to the length/type of the .wav files, some sounds need to be triggered in a 'loop' requiring a very short interval such as MGFIRE at 50 miliseconds HIGH, 50 miliseconds LOW, repeat as long as input PWM is at correct freq. Another .wav file needs a trigger (CANNONFIRE) that is HIGH for 50-100 miliseconds, but can not repeat for 6 seconds (so a delay() here would be terrible for the code)

8 ) also, the Tsunami is polyphinic, so that some .wav files will be running in the background(engine running), and a 'long' code with delays will interrupt the Nano pin ouput signals making the .wav sound interrupted and not so good.

  1. the Tsunami is 3.3v logic so I have to be careful and put in a level shifter (which means more wires and parts), or use a board such as the Teensy running at 3.3v

Significant Improvements down the road:

  1. decode the sbus signal via a microprocessor
  2. use same microprocessor to serial trigger the Tsunami .wav tracks

that looks like a cool project !

Hopefully, it going into this:

You picture for everyone to enjoy without downloading :slight_smile:

Thx for doing that JML! I don't think I can post pics yet...

OK so I included your section of code for the time out True/false and it works = Cool! I hooked the output pin to a wav board to test out cannonfire (using a wt588d). Cannon fire sound works fine. I changed the delay from 200 to 1000 so I could watch it on an LED too.

However:

There is 1000 milisec of serial dropout on channel PWM5 following CANFIRE trigger. So this pinout is HIGH for 1000ms. Not sure why the serial.print is dropping too. And it interrupts the other channels.

I tried changing the duration5/duration location but no change. It's like the PWM5 serial signal is not there at all. I changed it back to 200ms delay. I tried different forms/locations of serial.print duration5 /duration. This shortened the loop run time back to 200ms and thereby reestablishes the serialprint data 800ms faster.

So I am trying to figure out a way to keep WAV5 HIGH for 10ms and then not allowed to re-fire for 6 seconds without a delay() or serial monitor dropout, without losing channel function during the 'LOW' time. It is using your code but initiating it after CANFIRE is triggered.

10ms HIGH will trigger the .wav files to play so that is really the min time I need on HIGH. I loaded this on a Teensy3.2 as well as a Nano. No change in serial dropout.

[code]
int PWM1 = 2;
int PWM2 = 3;
int PWM3 = 4;
int PWM4 = 5; 
int PWM5 = 6; // cannonfire
int PWM6 = 7;

int WAV1 = 14;
int WAV2 = 15;
int WAV3 = 16;
int WAV4 = 17;
int WAV5 = 18; // cannonfire
int WAV6 = 19;

unsigned long duration;

unsigned long trigger5Time, duration5;
boolean busyFlag5 = false;
const unsigned int delay5 = 200; // 200 ms - an unsigned int is likely large enough for the delays (up to 65535ms = 65 seconds and a half roughly), otherwise use unsigned long



void setup()
{
  Serial.begin(115200);
  pinMode(PWM1, INPUT);
  pinMode(PWM2, INPUT);
  pinMode(PWM3, INPUT);
  pinMode(PWM4, INPUT);
  pinMode(PWM5, INPUT);
  pinMode(PWM6, INPUT); 

  pinMode(WAV1, OUTPUT);
  pinMode(WAV2, OUTPUT);
  pinMode(WAV3, OUTPUT);
  pinMode(WAV4, OUTPUT);
  pinMode(WAV5, OUTPUT);
  pinMode(WAV6, OUTPUT);  
}

void loop(){
{
  duration = pulseIn(PWM1, HIGH);
  Serial.print ("PWM1  ");
  Serial.print (duration);
  
}
{
  duration = pulseIn(PWM2, HIGH);
  Serial.print ("\tPWM2  ");
   if (duration > 1851 && duration < 1950)
  {
    digitalWrite(WAV2, HIGH);
    Serial.print ("T-Full  ");
  }
   
 else 
  {
  digitalWrite(WAV2, LOW);  
  Serial.print(duration); 
 
} 
}
{
  duration = pulseIn(PWM3, HIGH);
  Serial.print ("\tPWM3  ");
  Serial.print (duration);
 
}
{
  duration = pulseIn(PWM4, HIGH);
  Serial.print ("\tPWM4  ");
  Serial.print (duration); 
 
} 


if ((busyFlag5 == false) || (millis() - trigger5Time >= delay5)) { // if we are not triggered or if we are triggered and delay has expired 
  duration5 = pulseIn(PWM5, HIGH);
  Serial.print ("\tPWM5  ");
  if (duration5 >= 1851) {
    digitalWrite(WAV5, HIGH); 
    Serial.print ("CANFIRE");
    busyFlag5 = true;
    trigger5Time = millis();
    
  } else {
    digitalWrite(WAV5, LOW);
    Serial.print(duration5);
   busyFlag5 = false;
  }
}


{
  duration = pulseIn(PWM6, HIGH);
  Serial.print ("\tPWM6  ");

   if (duration >= 1851)
  {
    digitalWrite(WAV6, HIGH);
    
    Serial.println ("MGFIRE");
  }
   
 else 
  {
  digitalWrite(WAV6, LOW);
  
  Serial.println (duration); 

  }
}
}

[/code]

There is 1000 milisec of serial dropout on channel PWM5 following CANFIRE trigger. So this pinout is HIGH for 1000ms

The pulseIn() function here waits for the PWM5 pin to go from LOW to HIGH, starts timing, then waits for the pin to go LOW and stops timing and returns (an unsigned long) the length of the pulse in microseconds which you store in duration5.

The challenge might be that this event LOW TO HIGH transition does not happen and then the pulseIn() function gives up and returns 0 if no complete pulse was received within the timeout.

if you read the documentation, the call to the function can take a third argument which is the timeout --> the number of microseconds to wait for the pulse to start and the default is one second --> this is what you are seeing most probably.

OK so your state machine is a bit more complex that just doing a HIGH and coming back later... you need to read a bit about state machines and have a clear view on what should happen for each channel. Draw a state diagram (initially you are IDLE, you read a PWM and if it is in a range, you change state and do something and you can only exit this stage if some time has passed etc... you need to document all this.)

JML, So I went back and did some reading as you suggested. Decided to go with a switch/case and an automatic division of the PWM duration input. I edited my code and this works great and prints out appropriately on my serial monitor. It automatically divides the PWM signal into equal divisions of as many cases as I need. Since multiple divisions are only required for the 'engine sound .wav triggers. I am nearly done with this code for the minimal control that I need. The rest will be either high or low pinouts to the .wav board triggers. Eventually, once I get the sounds where I like them, these will be converted to serial control of the .wav board.

One drawback is that if my 'range' should vary out of the assigned const, then the map will shift all the 'case' divisions up or down appropriately. So I allowed a bit of laterality on the min/max. The other major drawback is that if I switch channels and have CANFIRE on 1 instead of 5 on my Tx, then I have to change the code(or just swap RMILEC output signal wires!), but that is not something I plan to change routinely. Alternatively, sbus Rx payload would be automatically defined, so that no wire or code switching would be necessary, but that is not really needed here on such a simple project.

A significant positive is the speed of the loop. No more appreciable delay! Just a matter of adding .wav trigger PINOUTs to appropriate cases. I am still working on a 5 sec post CANFIRE delay without delaying the loop....And I got rid of 80% of the {}'s!

I wrote these lines:

const int durationMin = 1000;
const int durationMax = 2000;

Since throttle is on PWM2 input, in place of above PWM2 duration section in the loop, I wrote this:

  duration = pulseIn(PWM2, HIGH);
  Serial.print ("\tPWM2  ");

  int range = map(duration, durationMin,durationMax, 0,8); 

  switch (range) {
      case 0: 
      Serial.print("FULLREV");
      break;
     
      case 1: 
      Serial.print("HIGHREV");
      break;
    
      case 2: 
      Serial.print("MEDREV");
      break;
      
      case 3: 
      Serial.print("LOWREV");
      break;

      case 4: 
      Serial.print("IDLE");
      break;
     
      case 5: 
      Serial.print("LOWFOR");
      break;
    
      case 6: 
      Serial.print("MEDFOR");
      break;
      
      case 7: 
      Serial.print("HIGHFOR");
      break;

      case 8:
      Serial.print("FULLFOR");
      break;

Ok , figured out the CANFIRE delay. I used millis() to set a timer and just timed a block of code ON and OFF. This does not interfere with the speed of code to any appreciable degree which is EXACTLY what I wanted to do. In this case, I was able to put a 3 second (instead of the 6 sec I was originally going to use) 'delay' into the CANFIRE portion of the code. I did have to use unsigned long on the OFF time. If it was not there and if I waited too long between CANFIRE triggers, the WAV5 output was held high too long on the next shot. It may not look pretty for the experts, and some of the code may be redundant, but it works great. Also, the way I have it set up, you can not re-fire the cannon unless you wait the full 3 seconds. If you pull the FIRE trigger too early, you are obviously resetting the 3 second egg timer! This may be a stressor of sorts in case you like to do IR tank battles, adds to the pressure of being the 'tank commander.' (but I really don't know, because I just build them and never use them!)

So basically, I have finished the code for the engine sounds and the code for the main gun fire 'delay.' The other 10-14 channels are on/off situations. Next step is to get the Tsunami sounds playing. But I think the Tsunami got lost in the mail..been 3 weeks and no show...

Check out the image, pretty cool to see it play out.

If anyone interested, here is the way I did the timer:

unsigned long previousMillis = 0;
unsigned long OffTime = 3000;
long OnTime = 20;

and in the loop at the read PWM5 :

  duration = pulseIn(PWM5, HIGH);
  Serial.print ("\tPWM5  ");
  digitalWrite(WAV5, LOW);

  unsigned long currentMillis = millis(); 
  if (duration >= 1800) 
  {    
    if (currentMillis - previousMillis >= OffTime)
      {     
      
      digitalWrite(WAV5, HIGH);
      previousMillis = currentMillis; 
      Serial.print("CANFIRE ");
      delay(20);
      }
      
      else if ( currentMillis - previousMillis >= OnTime)
      {
   
      digitalWrite (WAV5, LOW);  
      previousMillis = currentMillis;   
      Serial.print("RELOAD ");
     }
  }
      else if (duration <= 1799) 
      Serial.print(duration);

Great if that works for you !

Thx JML, Yes working Great(er)! I rewrote a bit of the program to make it smoother (always triggers PWM5). So now I have serialprint of RELOAD for 2.5 seconds(easily adjusted to longer time if needed). CANFIRE will serialprint appropriately too.

I was able to trim off a bit of the timer code, but had to add another ' else if.' It was interesting to learn a bit about how important it is to locate properly the timer 'start' as well as how to place the 'countdown' portion of the timer and of course the else if statments. I still have another 789 pages of C++ basics to read, but I figured I would try to start some programming early! Now I have to learn the serial commands for the TSunami wav board . I am sure there is always a better way to do this little code and real speed will be tested by listening for audible delays in wav file triggers throughout the code.

A variant of this code worked great to serialprint CANFIRE and then for 2.5 sec serialprint RELOAD. But, I had a bit of difficulty getting serialprint timed and WAV5 maintained LOW for 2.5 sec, and the CANFIRE timer all set up but eventually got it. It was because of how I set up the timer.

Important thing about the millis() timer is that in the current code version, if you hit the transmitter button for CANFIRE x2 in less than 2.5 sec, it will not reset the timer and not retrigger CANFIRE either. This was a big concern for me as you could be stuck in an endless 'egg timer reload' loop situation (I had earlier in above code) and the reload wav trigger may be sent several times without CANFIRE. All is well...for now.....

So I am still a bit confused, am I 'resetting' the millis() timer, or am I just cutting into a running timer and measuring the millis? This factor was the challenging part in terms of where to initiate the timer start , which does not happen until PWM5 pin high input >= 1800 . Since RELOAD serial prints for 2.5 sec on boot-up without following a CANFIRE trigger...I guess that the serialprint code for RELOAD begins, then is quickly followed by the timer start, and then loops so fast that the countdown seems appropriate (but probably is a few ms faster than 2.5 sec...) cool stuff....

so above void setup():

unsigned long reloadTime = 2500;
unsigned long startMillis;
unsigned long currentMillis;

and in void setup():

startMillis = millis()

and in the loop:

duration = pulseIn(PWM5, HIGH);
  Serial.print ("\tPWM5  "); 
     if (currentMillis - startMillis < reloadTime){
     Serial.print("RELOAD");
     digitalWrite(WAV5,LOW); 
          }
     else if (duration >= 1800){
     startMillis = currentMillis; 
          Serial.print("CANFIRE");
          digitalWrite(WAV5,HIGH); 
          delay(5); 
          }
     else if(duration <= 1799){
      Serial.print(duration); 
       digitalWrite(WAV5,LOW); 
      }

Hello All,

I have a few more questions.

Since last time, I have started on serial control of a Robertsonics Tsunami super wav board. The Tsunami is a polyphonic wav file player. It can play up to 32 tracks (mono) at one time off a C10 SD card. Signal from RC transmitter to receiver to sbus decoder (RMILEC) to Teensy3.2 then serial code to the Tsunami wav board. Also, when USB plugged in, serial print is to monitor, while serial Tx also goes to Tsunami via Tx1. No issues with this set-up so far.

I can trigger the Tsunami wav files with serial 1 wire Tx-Rx control via provided serial control commands (some shown below). The problem is that, especially for PWM2 (radio channel 2 throttle stick) engine sound block , the wav files are triggered each time through the loop and of course overflow the sound card buffer.

I do not yet know how to approach this using an array or a state machine/ for/while /if/else..etc.. I tried millis() but it became code cumbersome for each 'case' . I have read 1000 pages of C++ for beginners and read umpteen arduino code and forums pages and tried multiple code variants.

The goal is to have a series of engine sounds from IDLE , and then 5 steps of slightly higher RPM'S, and then FULL THROTTLE forwards (and same 5 steps and FULL THROTTLE for reverse). As I push up on the RC transmitter, the code currently steps through equally mapped divisions of about 75 Hz 'steps' from 1500 to 2000 PWM Hz(PPM) for each 'case' of the throttle stick position. Backwards push does the same thing from 1500-1000.

What I can't do is add a delay() during or after a wav track because the whole code slows down due to the way I created it. So I suspect that for each case, I will be fading out the previous wav track while fading in the next higher or lower track, and then stopping the original track, and repeating these steps on up or down the ladder, but within a self contained set of timed command structures.

Some of the basic Tsunami commands are (example is wav track 002_engine_idle aka 2, there are also accel 1-5 wav and FullThrot wav, and many other sounds, etc...). These are nearly instantaneous so no extra delay() necessary to trigger the Tsunami with the 'built-in' commands:

      tsunami.trackPlayPoly(2, 0, true); //play polyphonic track 2, 0 db 

      tsunami.trackLoop(2, true);

      tsunami.trackStop(2);

      tsunami.trackFade(2, -40, 1000, false);// fades track 2 to -40db over 1 sec,  
      
      tsunami.stopAllTracks();

And to fade in:

       tsunami.trackGain(2, -40);             // Preset Track 2 gain to -40dB
       tsunami.trackPlayPoly(2, 0, true);     // Start Track 2
       tsunami.trackFade(2, 0, 2000, false);  // Fade Track 2 to 0dB over 2 sec

Without any real experience, I do not know how to proceed. Any help appreciated!

Here is my code, please see the 13 steps of the PWM2 INPUT section in the loop:

#include <Tsunami.h>

Tsunami tsunami;

//these are all the PWM pin inputs from the RMILEC
//pin #s do not necessarily correlate with RC Tx channels
//but kept in order from 1-16 w/RMILEC(sbus converter) pin outs, only using 6

#define PWM1 2
#define PWM2 3 //engine sounds
#define PWM3 4
#define PWM4 5
#define PWM5 6// CANFIRE
#define PWM6 7// MGFIRE

// none of these are necessary, just for testing

#define WAV1 14
#define WAV2 15//Engine rpm sounds
#define WAV3 16
#define WAV4 17
#define WAV5 18// CANFIRE
#define WAV6 19// MGFIRE

const int durationMin = 1000;
const int durationMax = 2000;
unsigned long duration;


unsigned long MGFireTime;
unsigned long MGDelayTime = 250;
unsigned long reloadTime = 3000;
unsigned long startMillis;
unsigned long currentMillis;


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

  tsunami.start();
  delay(10);
  tsunami.stopAllTracks();

  startMillis = millis();

  //each PWM channel is assigned to one pin input
  //pin #s do not necessarily correlate with RC Tx channels
  //but kept in order from 1-16 w/RMILEC pin outs

  pinMode(PWM1, INPUT);
  pinMode(PWM2, INPUT);
  pinMode(PWM3, INPUT);
  pinMode(PWM4, INPUT);
  pinMode(PWM5, INPUT);
  pinMode(PWM6, INPUT);

  //none of these are necessary, just for testing

  pinMode(WAV1, OUTPUT);
  pinMode(WAV2, OUTPUT);
  pinMode(WAV3, OUTPUT);
  pinMode(WAV4, OUTPUT);
  pinMode(WAV5, OUTPUT);
  pinMode(WAV6, OUTPUT);
}

void loop() {

  currentMillis = millis();

  duration = pulseIn(PWM1, HIGH);
  Serial.print ("PWM1  ");
  Serial.print (duration);
  digitalWrite(WAV2, LOW);

  
duration = pulseIn(PWM2, HIGH);
  Serial.print ("\tPWM2  ");
  int range = map(duration, durationMin, durationMax, 0, 12);

  switch (range) {
    case 0:
      Serial.print("MAX RPM");
      digitalWrite(WAV2, HIGH);// lights an LED so I know this is working
      break;

    case 1:
      Serial.print("ACCEL5");
      break;

    case 2:
      Serial.print("ACCEL4");
      break;

    case 3:
      Serial.print("ACCEL3");
      break;

    case 4:
      Serial.print("ACCEL2");
      break;

    case 5:
      Serial.print("ACCEL1");
      break;

    case 6:
      Serial.print("IDLE");
      break;

    case 7:
      Serial.print("ACCEL1");
      break;

    case 8:
      Serial.print("ACCEL2");
      break;

    case 9:
      Serial.print("ACCEL3");
      break;

    case 10:
      Serial.print("ACCEL4");
      break;

    case 11:
      Serial.print("ACCEL5");
      break;

    case 12:
      Serial.print("MAX RPM");
      digitalWrite(WAV2, HIGH);// lights an LED so I know this is working
      break;
  }

  duration = pulseIn(PWM3, HIGH);
  Serial.print ("\tPWM3  ");
  Serial.print (duration);


  duration = pulseIn(PWM4, HIGH);
  Serial.print ("\tPWM4  ");
  Serial.print (duration);


  duration = pulseIn(PWM5, HIGH);
  Serial.print ("\tPWM5  ");
  if (currentMillis - startMillis < reloadTime) {
    Serial.print("RELOAD");
    digitalWrite(WAV5, LOW);
  }
  else if (duration >= 1800) {
    startMillis = currentMillis;
    Serial.print("CANFIRE");
    digitalWrite(WAV5, HIGH); // lights an LED
    delay(5);
    tsunami.trackPlayPoly(5, 0, true);
  }
 
  else if (duration <= 1799){
    Serial.print(duration);
    digitalWrite(WAV5, LOW);
  }


  duration = pulseIn(PWM6, HIGH);
  Serial.print ("\tPWM6  ");

  if  (currentMillis - MGFireTime > MGDelayTime) {
    tsunami.trackStop(6);

    if (duration > 1800) {
      MGFireTime = currentMillis;
      tsunami.trackPlayPoly(6, 0, true);
      tsunami.trackLoop(6, true);
      digitalWrite(WAV6, HIGH);
      Serial.println("MGFIRE");
    }
  }

  else (duration < 1799); {
    digitalWrite(WAV6, LOW);
    Serial.println(duration);
  }

}

Hello All,

I think I found a solution. Instead of millis , I tried bool . But to get the sound to play in the switch/case block I had to do this:

above set-up:

bool maxrpm = 0;

or this could be unsigned long or int which both worked, but not with true/false placed here(instead of 0 or 1) or in the loop, but an issue arises over about 30 sec of not playing a track(entering the 0 switch case).

in the loop:

    case 0:

      Serial.print("MAX RPM");
      digitalWrite(WAV2, HIGH);
      if (maxrpm == 0) {
        tsunami.trackLoop(2, 1);
        tsunami.trackPlayPoly(2, 0, true);
        maxrpm++;
      }
      break;

    case 1:
    
      Serial.print("ACCEL5");
      tsunami.trackStop(2);
      if (maxrpm >= 1) {
        maxrpm = 0;
      }
      break;

Now this code works for a time, then seems to go beyond a counter max and overloads the Tsunami input for track 2. If this works, this code would be easy to replicate for all cases as well as the other 12 channel triggers if I can figure out this issue. Also, this version code would not work with true/false substitutes at any level, only with 'maxrpm++' and if (maxrpm >= 1).

I think the maxrpm++ is counting and gets maxed out.. I also tried replacing maxrpm++ with maxrpm = 1. still overloading after about 30 sec...

any thoughts?