Executing multiple functions/commands on Arduino

2019-09-01 10:52发布

问题:

I am using an RFduino and an iOS application to control some RGB LEDs.

This is how I'm sending a string command to the module:

- (IBAction)fadeButtonPressed:(id)sender {
    [rfduino send:[@"fade" dataUsingEncoding:NSUTF8StringEncoding]];
}

These command(s) are coming back just fine on the RFduino side:

void RFduinoBLE_onReceive(char *data, int len) {
  if (strncmp(data, "fade", 4) == 0) {
    // begin fading chosen LED colour
  }
}

Is there a better way of executing multiple functions on Arduino? It seems to me that there should be a better way of doing what I'm trying to do.

Originally for example I was getting an issue where the "fade" string was coming back as "fadek" so I used strncmp(data, "fade", 4) instead of strcmp(data, "fade") and this fixed the issue.

I guess I'd like a way of cleaning up my code and perhaps make it easier to introduce new bits of functionality depending on which strings are coming back.

The functions I would like to be able to do would be controlling of the RGB colours and then Fading or Blinking that particular chosen colour.

What if I wanted to introduce faster blinking? Rather than setting another command integer and adding another condition is there a cleaner approach?

The selection of the colours is set by selection of a color wheel within my iOS application. This is working fine. The problem is that the Blinking and Fading does not blink/fade the selected colour (command 0).

Here is my entire sketch so far:

#include <RFduinoBLE.h>

// Pin 2 on the RGB LED.
int rgb2_pin = 2; // red
int rgb3_pin = 3; // green
int rgb4_pin = 4; // blue
int brightness = 0;
int fadeAmount = 5;

// Command properties.
int command = 0;

void setup() {
  // debug output at 9600 baud
  Serial.begin(9600);

  // Setup the LEDs for output.
  pinMode(rgb2_pin, OUTPUT);
  pinMode(rgb3_pin, OUTPUT);
  pinMode(rgb4_pin, OUTPUT);

  // This is the data we want to appear in the advertisement
  // (the deviceName length plus the advertisement length must be <= 18 bytes.
  RFduinoBLE.advertisementData = "rgb";

  // Start the BLE stack.
  RFduinoBLE.begin();
}

void loop() {
  if (command == 1) { // Fade in/out chosen colour.
    analogWrite(rgb2_pin, brightness);
    analogWrite(rgb3_pin, brightness);
    analogWrite(rgb4_pin, brightness);

    // Change the brightness for next time through the loop:
    brightness = brightness + fadeAmount;

    // Reverse the direction of the fading at the ends of the fade:
    if (brightness == 0 || brightness == 255) {
      fadeAmount = -fadeAmount ;
    }

    // Wait for 30 milliseconds to see the dimming effect
    delay(30);
  } else if (command == 2) { // Blink
    digitalWrite(rgb2_pin, HIGH);
    digitalWrite(rgb3_pin, HIGH);
    digitalWrite(rgb4_pin, HIGH);
    delay(200);
    digitalWrite(rgb2_pin, LOW);
    digitalWrite(rgb3_pin, LOW);
    digitalWrite(rgb4_pin, LOW);
    delay(200);
  }
}

void RFduinoBLE_onConnect() {}

void RFduinoBLE_onDisconnect() {}

void RFduinoBLE_onReceive(char *data, int len) {
  Serial.println(data);

  // Each transmission should contain an RGB triple.
  if (strncmp(data, "fade", 4) == 0) {
    command = 1;
  } else if (strncmp(data, "blink", 5) == 0) {
    command = 2;
  } else { // Change colour.
    // Reset other functions.
    command = 0;

    if (len >= 3) {
      // Get the RGB values.
      uint8_t red = data[0];
      uint8_t green = data[1];
      uint8_t blue = data[2];

      // Set PWM for each LED.
      analogWrite(rgb2_pin, red);
      analogWrite(rgb3_pin, green);
      analogWrite(rgb4_pin, blue);
    }
  }

  Serial.println(command);
}

回答1:

My approach to these sort of communications is to define a protocol that includes start and stop characters (say 0x01 and 0x03) and then build a state machine that processes each incoming byte.

The reason for this is it helps correct for out-of-sequence bytes and communication errors. You can ignore data until you get a 0x01 and the command doesn't end until you get a 0x03. If you get a 0x03 before you expect it then you can discard the invalid packet.

One issue you have with your current approach and this technique is that you are sending 8 bit data for the RGB command - this can conflict with your start/end bytes. It won't have much impact to encode your data as 2 digit hex, so you can have a protocol which looks something like

  • 0x01 - Start of packet
  • 1 byte command b=Blink, f=Fade, c=set color
  • 6 bytes arguments. For command c this would be three pairs of hex characters for rgb. For b & f it could be 2 characters of blink/fade rate with the other 4 bytes being 0000 for placeholder
  • 0x03 - End of packet

Then you can build a state machine -

  1. Waiting for 0x01. Once you get it move to state 2
  2. Waiting for a valid command byte. If you get a valid one move to state 3. If you get 0x01 move back to state 2. If you get any other byte move to state 1
  3. Waiting for 6 hex digits. If you get 0x01 stay in state 2. If you get anything other than 0-9 a-f move back to state 1
  4. Waiting for 0x03. If you get it then process complete command and return to state 1. If you get 0x01 move back to state 2. If you get anything else move to state 1

This won't compile as I don't have an Arduino in front of me, but you would use something like this

int state;   //  Initialise this to 1 
char command;
string hexstring;

void RFduinoBLE_onReceive(char *data, int len) {

    for (int i=0;i<len;i++) {
        stateMachine(data[i]);
    }
}

stateMachine(char data) {


    switch (state) {
       case 1:
          if (data == 1) {
              state=2;
          }
          break;
       case 2:
          if (data=='b' || data== 'f' || data == 'c') {  // If we received a valid command
             command=data;                               // store it
             hexstring="";                               // prepare to receive a hex string
             state=3;
          } else if (data != 1) {    //Stay in state 2 if we received another 0x01
             state =1;
          }
          break;
       case 3:
          if ((data >='a' && data <='z') || (data >='0' && data <='9')) {
              hexstring=hexstring+data;     // if we received a valid hex byte, add it to the end of the string
              if (length(hexstring) == 6) {  // If we have received 6 characters (24 bits) move to state 4
                  state=4;
              }
          } else if (data == 1) {            // If we received another 0x01 back to state 2
             state =2;
          } else {
             state=1;                        // Anything else is invalid - back to look for 0x01
          }
          break;
     case 4:
         if (data == 3)                      // 0x03=valid terminator
         {
           processCommand(command,hexstring);  // We have a valid command message - process it
           state=1;
         } else if (data==1) {               // 0x01= start of new message, back to state 2
           state=2;
         } else {
           state=1;                          // anything else, back to look for 0x01
         }
         break;
     }
}