Computer to Arduino (C++)

I am trying to implement Computer-to-Arduino communications. I started with this tutorial:
http://webhole.net/2010/05/01/arduino-visual-c-plus-plus-tutorial/

I modified the code to start working on what I need it to do - in this case choose whether to flash Red/Blue or Amber/White.

The code works - sort of. The issue is that it doesn’t loop, it only flashes each color, then waits. What am I missing?

C++ Code:

#include "stdafx.h"

using namespace System;
using namespace System::IO::Ports;

int main(array<System::String ^> ^args)
{

	String^ answer;
	String^ portName;
	int baudRate=9600;
	Console::WriteLine("Type in a port name and hit ENTER");
	portName=Console::ReadLine();
	// arduino settings
	SerialPort^ arduino;
	arduino = gcnew SerialPort(portName, baudRate);
	// open port
	try
	{
		arduino->Open();

		do
		{
			// ask on or off
			Console::WriteLine("Type \"on\" to turn the light on, \"off\" to turn it off, \"no\" to stop program...");
			// get answer
			answer=Console::ReadLine();
			//check that user typed one of the options
			if(String::Compare(answer,"on")==0)
				arduino->WriteLine("1"); // send 1 to arduino
			else if(String::Compare(answer,"off")==0)
				arduino->WriteLine("0"); // send 0 to arduino
			else
				Console::WriteLine(answer+" was not an option");
			Console::Clear();
		}while(String::Compare(answer,"no")!=0);
		// close port to arduino
		arduino->Close();
	}
	catch (IO::IOException^ e  )
	{
		Console::WriteLine(e->GetType()->Name+": Port is not ready");
	}
	catch (ArgumentException^ e)
	{
		Console::WriteLine(e->GetType()->Name+": incorrect port name syntax, must start with COM/com");
	}
	// end program
	Console::Write("Press enter to close the program");
	Console::Read();
    return 0;
}

Adruino Code:

const int r = 10;  //Red channel
const int g = 9;  //Green channel
const int b = 8;  //Blue channel

char mode = '1';    //Variable to hold flash mode (R/B or Y/W)
char lastMode;

void setup() 
{
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  
  Serial.begin(9600); // same as in your c++ script
  
  analogWrite(b, 255);  //Blue
  analogWrite(g, 255);  //Green
  analogWrite(r, 255);  //Red
}

void loop() 
{  
  lastMode = mode;
  
  if (Serial.available() > 0)
  {
    mode = Serial.read(); // used to read incoming data
  }
  else
  {
    mode = lastMode;
  }
  
  switch(mode)// see what was sent to the board
  {
    case '0':
      colorSelect('y');
      delay(100);
      colorSelect('o');
      delay(50);
      colorSelect('y');
      delay(100);
      colorSelect('o');
      delay(250);
      colorSelect('w');
      delay(100);
      colorSelect('o');
      delay(50);
      colorSelect('w');
      delay(100);
      colorSelect('o');
      delay(250);
      break;
    case '1':
      colorSelect('r');
      delay(1000);
      colorSelect('o');
      delay(250);
      colorSelect('b');
      delay(1000);
      colorSelect('o');
      delay(250);
      break;
  }
}


void colorSelect(char color)
{
  switch (color)
  {
    case 'y':
      analogWrite(r, 0);
      analogWrite(g, 0);
      analogWrite(b, 255);
      break;
    
    case 'w':
      analogWrite(r, 0);
      analogWrite(g, 0);
      analogWrite(b, 0);
      break;
      
    case 'r':
      analogWrite(r, 0);
      analogWrite(g, 255);
      analogWrite(b, 255);
      break;
      
    case 'b':
      analogWrite(r, 255);
      analogWrite(g, 255);
      analogWrite(b, 0);
      break;
      
    case 'o':
      analogWrite(r, 255);
      analogWrite(g, 255);
      analogWrite(b, 255);
      break;
  }
}

I understand the Arduino code is not particularly efficient - I intend to implement “Blink without Delay” later, right now I’m just trying to get this to work. Unless that’s part of the problem.

I appreciate your help. Thanks.

In visual studio you can add breakpoints (or use intellitrace in newer versions) to inspect why code does not “loop”. Just add a breakpoint and go step by step watching the variables, I am sure you will find the issue.

I'll try that when I get back to my computer, but I think I'm missing something in the Arduino code.

The C++ will loop, it's basically doing this

prompt for value -> send value -> prompt for value

I always get the prompt sitting there waiting for me, and every time I enter "on" or "off" (not very intuitive, but I'm trying to change the code slowly and it originally just turned LED 13 on and off) the RGB LED blinks the correct color pattern once and stops.

I think some part of the Arduino code is running the loop(), and then waiting for input. My guess is the problem is with the Serial.available() line waiting for a value instead of just looping if nothing is sent. Is that what it's doing/supposed to do, and if so is there a way to rewrite to get it to "fall through" unless a serial value is being sent?

const int b = 8;  //Blue channel

...

  analogWrite(b, 255);  //Blue

On the Uno pin 8 is not a PWM output.

My guess is the problem is with the Serial.available() line waiting for a value instead of just looping if nothing is sent.

No it doesn't wait. It tests if anything is available.

  lastMode = mode;

  if (Serial.available() > 0)
  {
    mode = Serial.read(); // used to read incoming data
  }
  else
  {
    mode = lastMode;
  }

Why not:

  if (Serial.available() > 0)
    mode = Serial.read(); // used to read incoming data

On the Uno pin 8 is not a PWM output.

That's my bad - it's still working, but I'm guessing that's because I'm using it digitally (it's only ever 0 or 255). I could actually rewrite everything to be digital out, since I'm not fading anything.

Why not:

Code: if (Serial.available() > 0) mode = Serial.read(); // used to read incoming data

That was actually the original usage, but wasn't working either. My technique was a (failed) attempt to use the last sent serial value in the case of no new data.

I'm still away from my computer (work computer won't let me install Arduino IDE), but I think I will add a default case to the switch to just turn on the LED if there is no other data. If the code is looping, it should light the LED after completing the blinking cycle. Maybe that will help isolate what's going on? Or add a check to the serial.read() to store the old value if the new value is invalid? That should eliminate any garbage readings.

I don't see anything particularly wrong with your Arduino code, but your PC code puzzles me:

    String^ answer;
    String^ portName;

I'm not familiar with the ^ symbol being used in that way. Have I missed something? Or is Microsoft doing something rather unusual?

I honestly have no idea, that part of the code is copy-paste. The last time I used C++ was in 2002, and I'm still learning the decade of advancements.

That's what I get for switching my major all those years ago, then picking a hobby a decade later that I could have used Computer Science for.

Maybe PaulS will know?

Apparently, it's a handle.

http://blogs.msdn.com/b/branbray/archive/2003/11/17/51016.aspx

Good guess on my part. Microsoft.

http://en.wikipedia.org/wiki/C%2B%2B/CLI#Handles

They know how to take something simple, and make it complex.

C++/CLI (Common Language Infrastructure) is a language specification created by Microsoft and intended to supersede Managed Extensions for C++. It is a complete revision that aims to simplify the older Managed C++ syntax, which is now deprecated. C++/CLI was standardized by Ecma as ECMA-372. It is currently available only in Visual Studio 2005, 2008, 2010 and 2012, including the Express editions.

Yeah, whatever.

Got it to work. Seems that the Arduino was reading serial data while the C++ was waiting for input. This code works (still didn't fix the PWM issue, but I will):

const int r = 10;  //Red channel
const int g = 9;  //Green channel
const int b = 8;  //Blue channel

char mode = '1';    //Variable to hold flash mode (R/B or Y/W)
char lastMode = '1';

void setup() 
{
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  
  Serial.begin(9600); // same as in your c++ script
  
  analogWrite(b, 255);  //Blue
  analogWrite(g, 255);  //Green
  analogWrite(r, 255);  //Red
}

void loop() 
{  
  if (Serial.available() > 0)
  {
    mode = Serial.read(); // used to read incoming data
  }

  if (mode != '0' && mode != '1')  //Check to see if the serial input was valid
  {
    mode = lastMode;  //If the input is invalid, keep doing the previous mode
  }
  
  switch(mode)// see what was sent to the board
  {
    case '0': //Amber/White double flash
      colorSelect('y');
      delay(100);
      colorSelect('o');
      delay(50);
      colorSelect('y');
      delay(100);
      colorSelect('o');
      delay(250);
      colorSelect('w');
      delay(100);
      colorSelect('o');
      delay(50);
      colorSelect('w');
      delay(100);
      colorSelect('o');
      delay(250);
      break;
    case '1': //Red/Blue single flash
      colorSelect('r');
      delay(1000);
      colorSelect('o');
      delay(250);
      colorSelect('b');
      delay(1000);
      colorSelect('o');
      delay(250);
      break;
    default:  //Purple solid (for testing)
      colorSelect('p');
      break;
  }
  
    lastMode = mode;  //Store whatever mode was in this loop for comparison
}


void colorSelect(char color)
{
  switch (color)
  {
    case 'y': //Yellow
      analogWrite(r, 0);
      analogWrite(g, 0);
      analogWrite(b, 255);
      break;
    case 'w': //White
      analogWrite(r, 0);
      analogWrite(g, 0);
      analogWrite(b, 0);
      break;
    case 'r': //Red
      analogWrite(r, 0);
      analogWrite(g, 255);
      analogWrite(b, 255);
      break;
    case 'b': //Blue
      analogWrite(r, 255);
      analogWrite(g, 255);
      analogWrite(b, 0);
      break;
    case 'o': //Off
      analogWrite(r, 255);
      analogWrite(g, 255);
      analogWrite(b, 255);
      break;
    case 'p': //Purple
      analogWrite(r, 0);
      analogWrite(g, 255);
      analogWrite(b, 0);
      break;    
  }
}

Where would the "garbage" serial data come from if the C++ was just waiting for input?

Not really related to the code, but to the overall project intent: Is there a way to use Serial input as an interupt? I would like the Arduino code to constantly cycle and only look for input if there is any.

The final project would be a Visual C++ program that controls the outputs of the Arduino. Most of the development will relatively isolated, my basic plan is to use one (matching) array on each side to control the states. The C++ will create the array and send it to the Arduino, which will read it and then essentially set the pins based on the value of the array. Is there any theoretical reason why this would not work?

Thanks for the help - I would not have caught the non-PWM pin, or been able to debug without first knowing for sure what serial.Available() was doing.

rogue_wraith: Is there a way to use Serial input as an interupt? I would like the Arduino code to constantly cycle and only look for input if there is any.

Serial input already is handled by an interrupt. You can be doing other stuff. If you want to modify the HardwareSerial library you could make it call your own function. But you get much the same result by testing Serial.available() from time to time.

That's what I thought the intent of serial.Available() was - if there's nothing there, skip and move on.

What has me confused is what the Arduino thought it was receiving while the C++ was waiting for input. Because something was happening, changing the value of "mode" to something other than '0' or '1'.

I think you'll find that Console.WriteLine() appends a linefeed to your string.

Your Arduino code does not validate the received character before saving it as the new mode so any unexpected character would set the mode to an invalid value.

Since you also don't validate the mode in the code that does the blinking, your sketch simply stops doing anything when it receives any invalid character.

I suggest you validate the received character before assigning it to mode.

I added validation functionality into the second iteration of the code I posted. I read the input and then check if its valid, and change it back to the last value if not. That worked that problem out.

I'll look into the Console.WriteLine() and see exactly what's going on there.

I think you'll find that Console.WriteLine() appends a linefeed to your string.

It's this code:

            if(String::Compare(answer,"on")==0)
                arduino->WriteLine("1"); // send 1 to arduino
            else if(String::Compare(answer,"off")==0)
                arduino->WriteLine("0"); // send 0 to arduino

The WriteLine() method is inherited from the same class as Console, and does output a carriage return and line feed, just like Serial.println() does. There is a Write() method that, like Serial.print(), does not output the CR/LF.

Thanks, PaulS. The Arduino version of C++ is relatively easy to me, since it is almost identical to the C++ I learned in high school/college. But...

The new Visual Studio stuff makes my head hurt, and it's going to get worse since the actual control program will need more than a command-line style interface.

I wonder if the carriage return was what was causing the problem with the loop, with it running the correct input the first loop(), then the carriage return the second time.

rogue_wraith: That's what I thought the intent of serial.Available() was - if there's nothing there, skip and move on.

It does mean that. You must be getting serial data when you don't think you are. Perhaps a logic analyzer on the pin?