Arduino State Machine

2019-08-31 03:49发布

问题:

This is a cross-post from someone who answered my original question here.

I'm not sure how to go about executing the 3 functions I'm after (as well as introducing even more than 3 in the future).

I am simply trying to Fade/Blink the selected Colour of an RGB LED (and perhaps introduce more functions in the future) where its RGB data is coming back from iOS and sent to an RFDuino BLE module.

Sends a "fade" string to the module picked up by RFduinoBLE_onReceive (char *data, int len) on the Arduino end.

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

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

Selected Color:

- (void)setColor
{
    NSLog(@"colors: RGB %f %f %f", red, green, blue);

    UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];

    [colorSwatch setHighlighted:YES];
    [colorSwatch setTintColor:color];

    uint8_t tx[3] = { red * 255, green * 255, blue * 255 };
    NSData *data = [NSData dataWithBytes:(void*)&tx length:3];

    [rfduino send:data];
}

This is originally how I set the RGB colour:

void RFduinoBLE_onReceive (char *data, int len) {
    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);
    }
}

This was the provided answer that now compiles on Arduino, but I have no idea how to actually execute my functions and where?

#include <RFduinoBLE.h>

int state;
char command;
String hexstring;

// RGB pins.
int redPin = 2;
int grnPin = 3;
int bluPin = 4;

void setup () {
  state = 1;

  pinMode(redPin, OUTPUT);
  pinMode(grnPin, OUTPUT);
  pinMode(bluPin, OUTPUT);

  // This is the data we want to appear in the advertisement
  // (the deviceName length plus the advertisement length must be <= 18 bytes.
  RFduinoBLE.deviceName = "iOS";
  RFduinoBLE.advertisementInterval = MILLISECONDS(300);
  RFduinoBLE.txPowerLevel = -20;
  RFduinoBLE.advertisementData = "rgb";

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

void loop () {
  //RFduino_ULPDelay(INFINITE);
}

void processCommand (int command, String hex) {
  // hex ?
  // command ?
}

void RFduinoBLE_onReceive (char *data, int len) {
  for (int i = 0; i < len; i++) {
    stateMachine(data[i]);
  }
}

void stateMachine (char data) {
  switch (state) {
    case 1:
      if (data == 1) {
        state = 2;
      }
    break;
    case 2:
      if (data == 'b' || data == 'f' || data == 'c') {
        command = data;
        hexstring = "";
        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 (hexstring.length() == 6) {
          state = 4;
        }
      } else if (data == 1) {
        state = 2;
      } else {
        state = 1;
      }
    break;
    case 4:
      if (data == 3) {
        processCommand(command, hexstring);
        state = 1;
      } else if (data == 1) {
        state = 2;
      } else {
        state = 1;
      }
    break;
  }
}

EDIT: Final code

#include <RFduinoBLE.h>

// State properties.
int state = 1;
char command;
String hexstring;

// RGB pins.
int redPin = 2;
int grnPin = 3;
int bluPin = 4;

// Setup function to set RGB pins to OUTPUT pins.
void setup () {
  pinMode(redPin, OUTPUT);
  pinMode(grnPin, OUTPUT);
  pinMode(bluPin, OUTPUT);

  // This is the data we want to appear in the advertisement
  // (the deviceName length plus the advertisement length must be <= 18 bytes.
  RFduinoBLE.deviceName = "iOS";
  RFduinoBLE.advertisementInterval = MILLISECONDS(300);
  RFduinoBLE.txPowerLevel = -20;
  RFduinoBLE.advertisementData = "rgb";

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

void loop () {
  switch (command) {
    case 1:
      // Blink.
    break;
    case 2:
      // Fade.
    break;
  }

  //RFduino_ULPDelay(INFINITE);
}

// Converts HEX as a String to actual HEX values.
// This is needed to properly convert the ASCII value to the hex
// value of each character.
byte getVal (char c) {
  if (c >= '0' && c <= '9') return (byte)(c - '0');
  else return (byte)(c - 'a' + 10);
}

// Process each function/command.
void processCommand (int command, String hex) {
  switch (command) {
    case 'b':
      command = 1; // Set blink mode.
    break;
    case 'f':
      command = 2; // Set fade mode.
    break;
    case 'c':
      // We put together 2 characters as is
      // done with HEX notation and set the color.
      byte red = getVal(hex.charAt(1)) + (getVal(hex.charAt(0)) << 4);
      byte green = getVal(hex.charAt(3)) + (getVal(hex.charAt(2)) << 4);
      byte blue = getVal(hex.charAt(5)) + (getVal(hex.charAt(4)) << 4);

      // Set the color.
      setColor (red, green, blue);
    break;
  }
}

// Sets the color of each RGB pin.
void setColor (byte red, byte green, byte blue) {
  analogWrite(redPin, red);
  analogWrite(grnPin, green);
  analogWrite(bluPin, blue);
}

// This function returns data from the radio.
void RFduinoBLE_onReceive (char *data, int len) {
  for (int i = 0; i < len; i++) {
    stateMachine(data[i]);
  }
}

// Main state machine function, which processes
// data depending on the bytes received.
void stateMachine (char data) {
  switch (state) {
    case 1:
      if (data == 1) {
        state = 2;
      }
    break;
    case 2:
      if (data == 'b' || data == 'f' || data == 'c') {
        command = data;
        hexstring = "";
        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 (hexstring.length() == 6) {
          state = 4;
        }
      } else if (data == 1) {
        state = 2;
      } else {
        state = 1;
      }
    break;
    case 4:
      if (data == 3) {
        processCommand(command, hexstring);
        state = 1;
      } else if (data == 1) {
        state = 2;
      } else {
        state = 1;
      }
    break;
  }
}

回答1:

There is some code here that you can use to convert hex characters to a byte.

So, you can add this to your existing code -

byte getVal(char c) 
{
       if (c >= '0' && c <= '9')
         return (byte)(c - '0');
       else
         return (byte)(c-'a'+10)
}

void processCommand (int command, String hex)
{
      switch (command) {
           case 'b':
              command = 1;   // set blink mode
               break;
           case 'f':
              command=2;    // set fade mode
              break;
           case 'c':
              byte red=getVal(hex.charAt(1)) + (getVal(hex.charAt(0)) << 4);
              byte green=getVal(hex.charAt(3)) + (getVal(hex.charAt(2)) << 4);
              byte blue=getVal(hex.charAt(5)) + (getVal(hex.charAt(4)) << 4);
              setColor(red,green,blue);
     }
}

void setColor(byte red,byte green,byte blue)
{

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

On the iOS side you can use something like this -

-(void) sendCommand:(char)command arg1:(Byte)arg1 arg2:(Byte)arg2 arg3:(Byte) arg3 {

    NSString *commandString=[NSString stringWithFormat:@"\001%c%02x%02x%02x\003",command,arg1,arg2,arg3];

    NSData *commandData=[commandString dataUsingEncoding:NSASCIIStringEncoding];

    [rfduino send:data];
}

- (IBAction)fadeButtonPressed:(id)sender {
    [self sendCommand:'f' arg1:0 arg2:0 arg3:0];
}

- (IBAction)blinkButtonPressed:(id)sender {
    [self sendCommand:'b' arg1:0 arg2:0 arg3:0];
}

- (void)setColor
{
    NSLog(@"colors: RGB %f %f %f", red, green, blue);

    UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];

    [colorSwatch setHighlighted:YES];
    [colorSwatch setTintColor:color];

    [self sendCommand:c arg1:red*255 arg2:green*255 arg3:blue*255];
}