Go Down

Topic: Sending Multiple Values over Serial (Read 1 time) previous topic - next topic

Mattix

Hello World!  Me and a friend are working on an arduino project where we're sending multiple values over serial (13 to be specific (14 if you count the carriage return)).  We're sending it with SerialPort and byte arrays through C# .Net 4.0 and then receiving it on an arduino mega which uses the messenger library.  We're having some problems receiving the data on the arduino side.  It receives the data, but it appears to be in the wrong order (i.e. values will be in different places each time).  I checked and it appears we're reading the same amount as we're receiving, so I don't see how it could get scrambled.

Thank you very much for the help.

Here's the code for arduino and C# respectively:

Code: [Select]
#include <Messenger.h>

Messenger message = Messenger();
//int pins[] = { 2,3,4,5,6,7,8,9,10,11 };

void setup() {
  Serial.begin(115200);
  message.attach(messageReady);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
 
  //for (int i = 0; i <11; i++)
    //pinMode(pins[i], OUTPUT);
}

void messageReady(){
  int xspeed = message.readInt();
  int xstrafe = message.readInt();
  int xhandvert = message.readInt();
  int xhandopen = message.readInt();
  byte xupdown = message.readChar();
  byte xsyringe = message.readChar();
  byte xdrill = message.readChar();
  byte xrotate = message.readChar();
  byte xdrillio = message.readChar();
  byte derp = message.readChar();
 
  Serial.println(xspeed);
 
 
  analogWrite(2, abs(xspeed));
}

void loop() {
  digitalWrite(3, HIGH);
  if (!Serial.available()) {
    return;
  }
  if (Serial.available()) {
    message.process(Serial.read());
  }
}


(Here's the C# program.  Sorry that it's so long.)
Code: [Select]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;

namespace ROV
{
    public partial class Form1 : Form
    {
        Controller controller;
        SerialPort port = null;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            controller = new Controller();
            controller.InputChanged += new Controller.ControllerHandler(controller_InputChanged);
            port = new SerialPort("COM15", 115200);
            if (!port.IsOpen)
            {
                port.Open();
            }
        }

        void controller_InputChanged(ControllerEventArgs e)
        {
            byte[] data = new byte[14]; //the data to send over the port

            float speedMap = map(controller.LS.Y, -1, 1, -255, 255);
            //byte speedTEST = (byte)speedMap;
            byte[] portSpeed = BitConverter.GetBytes((short)speedMap);
            lspeed.Invoke((Action)delegate { lspeed.Text = ((short)speedMap).ToString(); });
            data[0] = portSpeed[0];
            data[1] = portSpeed[1];
            //0-1

            #region Old Code
            //come back to this later
            //differentiate motor speeds
            //if negative, value will be added (subtracted) from left motor and subtracted (added) to right motor = net left rotation
            //if positive, inverse ^________________________________________________________________________________________________^
            //c# side.  do later

            //rotation changed to pulsing rotation with select/start
            //float rotationMap = map(controller.LS.X, -1, 1, -255, 255);
            //byte[] portRotation = BitConverter.GetBytes((short)rotationMap);
            //lrotation.Invoke((Action)delegate { lrotation.Text = ((short)rotationMap).ToString(); });
            #endregion

            float strafeMap = map(controller.LS.X, -1, 1, -255, 255);
            byte[] portStrafe = BitConverter.GetBytes((short)strafeMap);
            lstrafe.Invoke((Action)delegate { lstrafe.Text = ((short)strafeMap).ToString(); });
            data[2] = portStrafe[0];
            data[3] = portStrafe[1];
            //2-3

            float handVerticalMap = map(controller.RS.Y, -1, 1, -255, 255);
            handVerticalMap = -handVerticalMap; //this is to invert the value like flight simulation
            byte[] portHandVertical = BitConverter.GetBytes((short)handVerticalMap);
            larmVert.Invoke((Action)delegate { larmVert.Text = ((short)handVerticalMap).ToString(); });
            data[4] = portHandVertical[0];
            data[5] = portHandVertical[1];
            //4-5

            #region Old Code
            //phased out rs x usage on 3/19
            //float handExtensionMap = map(controller.RS.X, -1, 1, -255, 255);
            //byte[] portHandExtension = BitConverter.GetBytes((short)handExtensionMap);
            //larmExt.Invoke((Action)delegate { larmExt.Text = ((short)handExtensionMap).ToString(); });
            #endregion

            float handOpenCloseMap = controller.RT - controller.LT; //lt = open, rt = close
            handOpenCloseMap = map(handOpenCloseMap, -1, 1, -255, 255);
            byte[] portHandOpenClose = BitConverter.GetBytes((short)handOpenCloseMap);
            lhand.Invoke((Action)delegate { lhand.Text = ((short)handOpenCloseMap).ToString(); });
            data[6] = portHandOpenClose[0];
            data[7] = portHandOpenClose[1];
            //6-7

            byte portUpDown = controller.LB == Microsoft.Xna.Framework.Input.ButtonState.Pressed ? (byte)1 : (byte)0;
            portUpDown = controller.RB == Microsoft.Xna.Framework.Input.ButtonState.Pressed ? (byte)2 : portUpDown;
            lvertical.Invoke((Action)delegate { lvertical.Text = portUpDown == (byte)0 ? "Vertically Still." : (portUpDown == (byte)1 ? "Going up!" : "Going down!"); });
            data[8] = portUpDown;
            //8

            byte portSyringe = controller.B == Microsoft.Xna.Framework.Input.ButtonState.Pressed ? (byte)2 : (byte)0;
            portSyringe = controller.A == Microsoft.Xna.Framework.Input.ButtonState.Pressed ? (byte)1 : portSyringe;
            lsyringe.Invoke((Action)delegate { lsyringe.Text = portSyringe == (byte)0 ? "Syringe Inactive." : (portSyringe == (byte)1 ? "Filling!" : "Emptying!"); });
            data[9] = portSyringe;
            //9

            byte portDrill = controller.X == Microsoft.Xna.Framework.Input.ButtonState.Pressed ? (byte)1 : (byte)0;
            ldrill.Invoke((Action)delegate { ldrill.Text = portDrill == (byte)0 ? "Drill inactive." : "Drill active"; });
            data[10] = portDrill;
            //10

            byte portRotate = controller.Select == Microsoft.Xna.Framework.Input.ButtonState.Pressed ? (byte)1 : (byte)0;
            portRotate = controller.Start == Microsoft.Xna.Framework.Input.ButtonState.Pressed ? (byte)2 : portRotate;
            lrotation.Invoke((Action)delegate { lrotation.Text = portRotate == (byte)0 ? "Not rotating." : (portRotate == (byte)1 ? "Rotating left." : "Rotating right."); });
            data[11] = portRotate;
            //11
            //those are the mappable buttons so we need to map them to something for them to work.
            //I suggest mapping lb2 to select and rb2 to start

            byte portDrillIO = controller.Up == Microsoft.Xna.Framework.Input.ButtonState.Pressed ? (byte)1 : (byte)0;
            portDrillIO = controller.Down == Microsoft.Xna.Framework.Input.ButtonState.Pressed ? (byte)2 : portDrillIO;
            lDrillIO.Invoke((Action)delegate { lDrillIO.Text = portDrillIO == (byte)0 ? "Not moving." : (portDrillIO == (byte)1 ? "Extending." : "Retracting."); });
            data[12] = portDrillIO;
            //12

            data[13] = ASCIIEncoding.ASCII.GetBytes("\r")[0];

            //port.Write(new byte[] { speedTEST }, 0, 1);
            port.Write(data, 0, 14);
            port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
            //ldata.Invoke((Action)delegate { ldata.Text = BitConverter.ToString(data); });
            //char[] buffer = new char[13];
            //if (port.BytesToRead > 0)
            //{
            //    ldata.Invoke((Action)delegate
            //    {
            //        port.Read(buffer, 0, 13);
            //        ldata.Text = new string(buffer); //this is to stop epic, hardcore, vomit-inducing lag
            //    });
            //}
        }

        void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            ldata.Invoke((Action)delegate
            {
                ldata.Text = port.ReadExisting();
            });
        }

        float map(float x, float in_min, float in_max, float out_min, float out_max)
        {
            return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            //because of the polling thread, app doesn't close when exit button is hit.
            //this fixes that
            controller.ExitPollingThread();
            Application.Exit();
        }
    }
}


PaulS

Code: [Select]
  if (!Serial.available()) {
    return;
  }

Useless code. Get rid of it.

The Messenger library expects ASCII data. You are not sending ASCII data from the C# program to the Arduino. Why do you think that you can then use the Messenger library?

Mattix

Ok, thanks for the advice. What would be a better way of receiving the data?

PaulS

Quote
What would be a better way of receiving the data?

Use Serial.read() to collect the bytes. Re-assemble as ints as required.

l4tran

Hello, I'm new to Arduino and I have a similar problem.  I'm sending exactly 15 characters to the Arduino via XBee pro.  I think the XBee pro is setup correctly because if I send exactly 15 characters at one time, the Arduino can read it perfectly.  However if I start sending it multiples times, I get erratic data.  The format of my data being sent is as follows "xxx:yyy:nnn:sss"  I tried to implement a checksum to only get the right data where sss = xxx+yyy+nnn, but this only works 95-97% of the time which is not good enough.  I think by coincident, sometimes sss = xxx+yyy+nnn.  This 3-5% error mess up my entire project.  I'm using a language called Autoit to communicate to the Arduino.  Any help is appreciated.

Code: [Select]

#include <SoftwareSerial.h>
#include <SabertoothSimplified.h>

#define SABER_TX 13
#define XBEE_RX 2
#define XBEE_TX 3
#define PARAMETERS 4

SoftwareSerial SWSerial(NOT_A_PIN, SABER_TX);
SoftwareSerial XBee(XBEE_RX, XBEE_TX);
SabertoothSimplified ST(SWSerial);

// Parse command from XBee depending on how many parameters.
// Each parameter must be 3 characters in length. Delimeter of ':' must be used between 2 or more parameters.
// Single or double digit must be padded with zeroes. ie: "003:030:033"
// If 2 parameters format is "xxx:yyy". 3 parameters format is "xxx:yyy:nnn" etc...
// Since we have 4 parameters the INPUT_SIZE is 4*4-1 = 15 "xxx:yyy:nnn:sss"
// Hence we read in 15 characters at a time from XBee Serial.

void GetCommand() {                                     
  int INPUT_SIZE=PARAMETERS*4-1, i=0, val[PARAMETERS]; 
  int checksum=0;                                       
  char input[INPUT_SIZE + 1];                           
  byte size = XBee.readBytes(input,INPUT_SIZE);         
  input[size]=NULL;                                     
                                                           
  char* command = strtok(input, ":");                   // Split string with ':' delimeter and store to array.
  while (command != NULL) {                                                                     
    val[i]=atoi(command);                               
    i++;
    command = strtok (NULL, ":");                     
  }
  for (int i=0;i<PARAMETERS-1;i++){                    //Calculate checksum to make sure data is correct.
    checksum+=val[i];                                   
  }
 
  if (checksum==val[PARAMETERS-1]){                             
    timeOfLastGoodPacket = currentTime;
    ST.drive(val[1]-127);                               //Since the motor driver expects range from -127(reverse) to +127(forward)
    ST.turn(val[0]-127);                                //We must subtract -127 because the value received range from 0 to 254.     
    delay(10);
    if (val[2]) {XBee.write("200");}               //Send back to get next data stream
    Serial.print('\n');
    Serial.print(val[0]-127); Serial.print(',');
    Serial.print(val[1]-127); Serial.print(',');
    Serial.print(val[2]); Serial.print(',');
    Serial.print(val[3]-254);
  }
}






Robin2

You have tagged onto a Thread that has been dormant for 3 years and which seems to involve a Messenger library that you are not using.

serial input basics has simple and reliable functions to receive data. There is also a parse example.

There is another current Thread about the Sabertooth Library and SoftwareSerial. It may be that they are incompatible. It would make more sense to read or maybe even join in with that Thread

...R

Go Up