I'm having USB communication issues between XIAO SAMD21 and a VS Forms app

I have created a forms app to send 2 commands to the XIAO and receive 5 possible status codes from the XIAO. the send commands appears to be functioning (i see the receive LED blink on the XIAO) , however the status is not displayed on the form as expected.

I have the form app set up for 9600 baud, 8 data bits, one stop bit and no parity which matches the NANO. I cannot find the requirements of the XIAO.

I tested the form using the same Arduino sketch on a NANO and everything works as expected.

I tested the XIAO using the Arduino IDE serial monitor and everything works flawlessly.

Is there a difference in how the serial data is sent from the XIAO ? is there a difference is the serial setup requirements for the XIAO that are somehow handled by the Arduino serial monitor but not my forms app?

Hi there,

So, on the SAMd21 and without seeing the code in question or a picture of the DUT, you need to explicitly name the Serial port pins to use.
Address it with the GPIO numbers and you should be good to go. :+1:

HTH
GL :slight_smile: PJ :v:

Also which serial port are you using in the code? Serial.print(…) or Serial1.print(…)
you may have them swapped. :+1: forms may use the uart by default btw.

I have found with the XIAO SamD21 in some cases (ie applications), I need to assert the DTR signal to allow the (USB) Serial to communicate with my applications. The Serial1 works without this requirement (via the XIAO TX and RX and not via the USB).

https://files.seeedstudio.com/wiki/Seeeduino-XIAO/res/XIAO-SAMD21-pinout_sheet.xlsx

i’m using Serial.print and the USB connection to the PC. I avoided using pins 6 &7 for anything because they are listed as the UART so i assumed they were tied to the USB, but cannot find anything that specifically tells me that.

I had no idea what you mean by assert the DTR, i have googled it and i think i have it covered with while (!serial){} in the setup section of my sketch. I’ll attempt to post the code below if that helps.

Here is the Arduino side of the code

/*******************************************************************************

 * DESCRIPTION:

 This sketch is written to control a DC Gearmotor that swings a Flat panel into position against the dew shield of the telescope(Closed) and back against the wall (Open). 




 The DC Gearmotor is Controlled by a Cytron DC motor Controller which requires speed input via a pulse Width Modulated signal 

 on the PWM terminal and Direction via a binary input on the DIR terminal. These inputs to the motor controller will

 originate from the Seeed Studio XIAO SAMD21 Board. Note the I/O pins are only rated for 3.3V

 

 Final speed of the motor will be determined during commisionaing and controled by this sketch using the MaxSpeed varible.(0-255)




 The commands required for the motor controller were derived from the Example scipt and library created by:

          AUTHOR   : Kong Wai Weng

          COMPANY  : Cytron Technologies Sdn Bhd

          WEBSITE  : www.cytron.io

          EMAIL    : [email protected]

          

          the library needed for this board is at 
 but should be able to be installed diectly from the library manager.




  Control inputs will be recieved by serial control via a Windows Form App




 information on the Arduino Seeed XIAM Samd21 board can be found here.  


 To install the Samd board in the Arduino IDE Click on File > Preference, and fill Additional Boards Manager URLs with the url below:




      https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json




  The commands from PC to Arduino will be OpenCover, Closecover. This Arduino sketch will report Status using the following Enumerators;

  1= Closed, 2= Moving, 3=Open,4=Unknown, 5=Error.

 




  This Version adds local manual pushbutton switches to also control the mechanism.

 

 * CONNECTIONS:

 

  Arduino D1  - Grd thru a N/O limit switch to input for stopping in the COVER_ON direction

  Arduino D2  - Grd thru a N/O limit switch to input for stopping in the COVER_OFF direction

  Arduino D3  - Motor Driver PWM Input

  Arduino D4  - Motor Driver DIR Input

  Arduino D9  - Open Pushbutton

  Arduino D10 - Close Pushbutton

  Arduino GND - Motor Driver GND

  5V pwr and Grd and serial communication via USB

  24VDC power to the Cytron motor controller and output wiring from the controller to the motor

 

************************************************************************************************************************************************/




#include "CytronMotorDriver.h" //this loads the required library for the cytrom motor control




// Configure the motor driver.

CytronMD motor(PWM_DIR, 3, 4);  // PWM = Pin 3, DIR = Pin 4.




//This section sets up the varibles needed for serial communication

String serialin; //declares a string type variable called "serialin" to store incoming serial data from the serial line





//This section sets up other global varibles

int Speed = 0; //creates a integer varible "Speed" and assigns the initial value of 0

int MaxSpeed = 128; //creates an integer varible "MaxSpeed" and assigns a value (0-255), adjust to get desired travel speed

unsigned long end_time;//  declares an unsigned, long type varible named 'end_time' a unsigned long varible is 32 bits (0 to 4,294,967,295) (intial Value to be assigned in a few lines)

unsigned long ActuatorTravelTime = 5000; //change to suit your rquirments , set it to just longer than expected open or close time (in milliseconds) to determine if Actuator is lost depending on Actuator travel time.

int TravelRemaining ; //create a varible to store remaining run time to Error

int Switch_Input = 0 ; //Declares a integer varible called "Switch_Input" to store the state of the switch and assigns the initial value of 0




// The setup routine runs once when you press reset.

void setup() {




  // this section defines the input pins

 pinMode(1, INPUT_PULLUP);  // assigns pin 1 as INPUT with an internal pullup resistor

 pinMode(2, INPUT_PULLUP);  // assigns pin 2 as INPUT with an internal pullup resistor

 pinMode(9, INPUT_PULLUP);  // assigns pin 9 as INPUT with an internal pullup resistor

 pinMode(10, INPUT_PULLUP);  // assigns pin 10 as INPUT with an internal pullup resistor





 end_time=millis()+ActuatorTravelTime; //Actuator lost reset timer 'end_time' = current time +ActuatorTravelTime seconds




 

  Serial.begin(9600); //Begin Serial Comunication(configured for 9600 baud) using default values of 8Bit, no parity, 1 stop bit

    while (!Serial) {  ;// Wait for serial port to connect. Required for native USB!

    }

  serialin = "";  //clears the serialin 




  // Make sure the RX, TX, and built-in LEDs don't turn on, they are very bright!

  // Even though the board is inside an enclosure. Unfortunately, it is not possible to turn off the power LED (green) in code...

 // pinMode(PIN_LED_TXL, INPUT);

  //pinMode(PIN_LED_RXL, INPUT);

 // pinMode(LED_BUILTIN, OUTPUT);

  //digitalWrite(LED_BUILTIN, HIGH);




}





// The loop routine runs over and over again forever.

void loop() {




 Switch_Input = 0; //Clears the varible Switch_Input




 int Open_Limit =digitalRead(1); //creates an integer varible "Open_Limit" and reads pin 1 status into that varible (HIGH if limit switch is open(off), LOW if limit switch is closed(on))

 int Close_Limit =digitalRead(2); //creates an integer varible "Close_Limit" and reads pin 2 status into that varible (HIGH if limit switch is open(off), LOW if limit switch is closed(on))

 int Open_PB =digitalRead(9); //creates an integer varible "Open_PB" and reads pin 9 status into that varible (HIGH if pushbutton switch is open(off), LOW if pushbutton switch is closed(on))

 int Close_PB =digitalRead(10); //creates an integer varible "Close_PB" and reads pin 10 status into that varible (HIGH if pushbutton switch is open(off), LOW if pushbutton switch is closed(on))




 if ((Open_Limit ==LOW) && (Close_Limit == LOW)){ //both switches should never be closed unless one is stuck or there is a short in the wiring

    Serial.println(5); //notify the PC app of ERROR via serial interface

    while((Open_Limit == LOW  && Close_Limit == LOW)){}//get stuck in this while loop that can only be reset by fixing the broken limit circuit or by restarting/resetting

  }




 if (Open_PB == LOW) { //if the open pushbutton has been pressed

   Switch_Input = 1; //Assigns the value "1" to the Switch_Input varible

  }

 if (Close_PB == LOW) {//if the Close pushbutton has been pressed

   Switch_Input = 2; //Assigns the value "2" to the Switch_Input varible

  }




 //=======================================================================================================

 //look for and act on commands on the serial interface  




 while ((Serial.available()>0) || (Switch_Input > 0)) {  //will loop through these commands until there is no data remaining in the serial recieve buffer

    

   serialin = Serial.readStringUntil('#'); //Read Serial data and store the results in the 'serialin' string variable until it recieves an '#' 

 

   if ((serialin == "CloseCover") || (serialin == "CLOSE") || (Switch_Input == 2)) { //if the command 'CloseCover' or 'CLOSE' is recieved, or the close switch has been pressed

     

     //Actuator status check, checks to see if the position is known and resets end_time

     //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

     if ((Close_Limit == LOW) || (Open_Limit == LOW)){ // if either switch reads as a CLOSED contact

         end_time=millis()+ActuatorTravelTime; //reset the timer 'end_time'

      }// end of if Actuator status known

     //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  




     // this section puts the COVER ON(Close) if the Close command has been recieved and the associated Limit Switch is open

     while (Close_Limit == HIGH) {

        Open_Limit =digitalRead(1); //reads pin 1 status into that varible (HIGH if limit switch is open(off), LOW if limit switch is closed(on))

        Close_Limit =digitalRead(2); //reads pin 2 status into that varible (HIGH if limit switch is open(off), LOW if limit switch is closed(on)

        Speed = MaxSpeed; //set speed varible to max speed

        motor.setSpeed(Speed);  // Run forward at Maxspeed




        //**********************

        //This section evaluates if the motor has been running longer than expected(end_time)

        //and if so, shuts the motor off , prints Error on serial monitor and locks out the sketch until reset

        TravelRemaining= end_time-millis(); //calculates the remaining time before Error

        Serial.println(2); //Report the moving status to the PC App




        if(millis()>=end_time) { //I.E. >2 s have passed since program started or since 'end_time' was last reset!!

          motor.setSpeed(0);    // Stop the motor (it may be stalled)

          Serial.println(5); //let the PC know of the ERROR because the actuator is lost

          while(millis()> end_time){}//get stuck in this while loop that can only be reset by restarting/resetting

        } //end of if Actuator is lost

        //***********************

      } //end of while loop

     Speed = 0; // sets speed varible back to 0

     motor.setSpeed(Speed);  // Stop motor now that limit switch has closed

    } //end of if 'CloseCover'




    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~




   if ((serialin == "OpenCover") || (serialin == "OPEN") || (Switch_Input == 1)){ //if the command 'OpenCover' or 'OPEN' is recieved, or the open switch has been pressed

        

     //Actuator status check, checks to see if the position is known and resets end_time

     //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

     if ((Close_Limit == LOW) || (Open_Limit == LOW)){ // if either switch reads as a CLOSED contact

         end_time=millis()+ActuatorTravelTime; //reset the timer 'end_time'

      }// end of if Actuator status known

     //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  




     // this section takes the COVER Off(Open) if the OPEN command is recieved and the associated Limit Switch is open

     while (Open_Limit == HIGH) {

       Open_Limit =digitalRead(1); //reads pin 1 status into that varible (HIGH if limit switch is open(off), LOW if limit switch is closed(on))

       Close_Limit =digitalRead(2); //reads pin 2 status into that varible (HIGH if limit switch is open(off), LOW if limit switch is closed(on))

       Speed = 0 - MaxSpeed; //set speed varible to negative maxspeed

       motor.setSpeed(Speed);  // Run reverse at Maxspeed




       //***********************

       //This section evaluates if the motor has been running longer than expected(end_time)

       //and if so, shuts the motor off , prints Error on serial monitor and locks out the sketch till reset

       TravelRemaining= end_time-millis(); //calculates the remaining time before lost

        Serial.println(2); //Report the moving status to the PC App




       if(millis()>=end_time) { //I.E. >2 s have passed since program started or since 'end_time' was last reset!!

         motor.setSpeed(0);    // Stop the motor (it may be stalled)

         Serial.println(5); //let PC app know that the actuator is lost

         while(millis()> end_time){}//get stuck in this while loop that can only be reset by restarting/resetting

       } //end of if Actuator is lost

       //***********************

      } //end of while loop

      Speed = 0; // sets speed varible back to 0

      motor.setSpeed(Speed);  // Stop motor now that the limit switch has closed

    } //end of if 'OpenCover' 




   serialin = "";  //clears the serialin 




  } //end of while serial loop




//==============================================================================================================




 // The next three sections determine the status of the Cover

   

 if ((Close_Limit == 1) && (Open_Limit == 0)){ //if 'closed' switch is OPEN and 'open' switch is CLOSED

   Serial.println(3); //updates the status as OPEN

 } //end of if 'closed'




 if ((Close_Limit == 0) && (Open_Limit == 1)){ //if 'closed' switch is CLOSED and 'open' switch is OPEN

   Serial.println(1); //updates the status as CLOSED

 } //end of if 'closed'

 

 if ((Close_Limit == 1) && (Open_Limit == 1)) { //if both switches are OPEN

   Serial.println(4); //updates the status as unknown

 } //end of if 'closed'




   delay(250 );

    /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    //This section is commented out if using ASCOM, but can be turned on for troubleshooting or when using Arduino serial control

    Serial.println(); //print several lines to clear the serial monitor

    Serial.println(); //print several lines to clear the serial monitor

    Serial.println(); //print several lines to clear the serial monitor

    Serial.println(); //print several lines to clear the serial monitor

    Serial.println(); //print several lines to clear the serial monitor

    //Serial.print("Close Limit is "); //prints text listed on Serial monitor

    //Serial.println(Close_Limit);  //prints the varible

    //Serial.print("Open Limit is "); //prints text listed on Serial monitor

    //Serial.println(Open_Limit);  //prints the varible

    delay (100);




   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

    




} //end of void loop





  /*

  commands from the Cytron Example sketch 

  motor.setSpeed(128);  // Run forward at 50% speed.

  delay(1000);

  

  motor.setSpeed(255);  // Run forward at full speed.

  delay(1000);




  motor.setSpeed(0);    // Stop.

  delay(1000);




  motor.setSpeed(-128);  // Run backward at 50% speed.

  delay(1000);

  

  motor.setSpeed(-255);  // Run backward at full speed.

  delay(1000);




  motor.setSpeed(0);    // Stop.

  delay(1000);

  */

this is the VS forms side of the communication. Note that I manually set the COM port in SerialPort1 properties before building/re-building the solution.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace My_Cover_Controls
{
    public partial class Form1 : Form
    {
        public delegate void d1(string indata);
        private static string status;
        public Form1()
        {
            InitializeComponent();
            try { serialPort1.Open(); }
            catch (Exception ex) { MessageBox.Show(ex.Message); }
        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {

        }

        private void OpenButton_Click(object sender, EventArgs e)
        {
            //Send command to Arduino to Open the Cover
            serialPort1.Write("OpenCover");
        }

        private void CloseButton_Click(object sender, EventArgs e)
        {
            //Send command to Arduino to Close the cover
            serialPort1.Write("CloseCover");
        }

        
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
            {
                string indata = serialPort1.ReadLine();
                d1 writeit = new d1(Write2form);
                Invoke(writeit, indata);
            }
        public void Write2form(string indata)
        {
            //This function handles data from the Arduino
            char firstchar;
            firstchar = indata[0];
            switch (firstchar)
            {
                case '1':
                    status = "Closed";
                    textBox1.Text = status;
                    break;
                case '2':
                    status = "Moving";
                    textBox1.Text = status;
                    break;
                case '3':
                    status = "Open";
                    textBox1.Text = status;
                    break;
                case '4':
                    status = "Unknown";
                    textBox1.Text = status;
                    break;
                case '5':
                    status = "Error";
                    textBox1.Text = status;
                    break;
                default:
                    status = "Not Present";
                    textBox1.Text = status;
                    break;
            }
        }
        
    }
}

Apologies…
You’d probably need to set the DTR state in the “Forms” app. I’ll have a look and try see how to do it (don’t really use VS much any more).

Edit> AI says this…

            // Set DtrEnable to true BEFORE opening the port
            serialPort1.DtrEnable = true;
1 Like

DUDE! I have spent a week troubleshooting this, googling, reading, modifying, making changes, testing. I spent 2 days just trying to get accepted at another site, When i posted the question it was voted down and removed within a couple hrs. Now in a matter of hours, and one simple line of code and it is working (on the bench at least) I’ll install and do final testing tomorrow, but i am pretty confident that you solved it.

Thank You

3 Likes

SWEET! Glad you got something working :slight_smile:

1 Like

Soooo. It is Almost fixed. The code provided by grobasoz above, got the status to appear in textbox1, however it does not update properly. i’ll try to explain below what my testing revealed after re-installing the Seeed in the actual mechanism.

When I first open the App, the Cover Status appears correctly matching the current cover position. (Open or Closed, I.E. 3 or 1) .

If i Press either an App buttons on screen or the hardwired physical buttons on the mechanism, it will open or close as expected AND the Cover Status will update to Moving (2). However, once the mechanism finishes it’s trave, l the status is not updated to Open or Closed as expected, it remains Moving.

I can still operate the cover using the buttons, but the status will remain frozen at Moving.

If i then close the App and re-open it, it will now display the correct status again.

However, Note that the Close X button on the windows app does not close the app, I have to right click the related object in the taskbar and select “Close Window”. I doubt this is related but it may be, because the Close X will sometime work when the app is first opened, but not always.

I did re-test with the Arduino Serial monitor and everything operates as expected. so it is still something in the forms app related to the Seeed that is not working. I placed the DTR code line recommended right after “Initialize Component() and before the SerialPort1.open() command. maybe it needs to be somewhere else to refresh it often?

            InitializeComponent();
            serialPort1.DtrEnable = true;
            try { serialPort1.Open(); }
            catch (Exception ex) { MessageBox.Show(ex.Message); }
1 Like

Unfortunately, Claude said your code would not work as expected, and may crash.

So, I installed VS2026 and asked GitHub CoPilot (in VS2026) to create a simple WinForms C# application. It works as expected on both ports (USB and XIAO TX/RX).

It may be beneficial for you to monitor the serial comms somehow.
I use either 2x USB-Serials (RX only used for each RX and TX from the XIAO), or a Digital Oscilloscope (with RS232 decode). BUT that can’t be used for USB interfaces.

Another option is using a Virtual serial port (eg VSPE) in the case where you use USB Serial…

It may pay to use the XIAO TX and RX and Serial1 interface to see if that is functioning differently? I use XIAO SamD21 TX and RX at 115200 baud with no issues on one of my projects. That way you can monitor the comms?

Finally you should be able to run your forms app using the debugger to determine what is happening?

it works as expected with the Arduino serial monitor, i was using the debug when it was on the bench but i have no clue what im doing in vs, so it ran but i didnt see any extra info that helped me ts. I tried the serial monitor in VS but it cant be run in parallel with the vs form so it was useless to me. I have now re-installed the Seeed board in the project which is connected to a different pc than the one with VS installed.

i just tried the git hub chat, i said i was having serial issues with my form and without any further clarification or interaction, it added a bunch of serial related code. i re-built the solution but the new form does the exact same things as the old form.

I did get a spare Seeed in the mail yesterday , so maybe later I can upload the code and try some more bench testing with it.

1 Like

I don’t have a Cytron DC motor Controller but can test your (latest?) Arduino code on my ESP32-C6 development system.

At this stage however I don’t think your Arduino code is working as expected. The reason for the “hanging” or “crash” is that the serial port is being flooded by messages during state changes.
So, when “Moving”, the Serial.println(2) message is being sent continuously. I’d suggest changing that to a more suitable structure to only report once every 250-500mS depending on your motor type (small motors can have faster responses).

I’d also recommend using a ramp up and ramp down on the speed changes if possible to protect the driver and motor.

Edit> There is also an error on the reading of the switch/button states…

1 Like

Thanks, i was using the AI assist in VS this morning, it made a bunch of changes to my app and now the cover status isn’t reported at all anymore. But while explaining the issue to the ai, it occurred to me that the moving status was being sent too fast. I’ll work on those suggestions and see if it helps with the previous version of my app.

1 Like

Yes, that’s what I had started typing earlier but had to go work in the garden (Sunday) :stuck_out_tongue:

I suggest looking a little closer at the Arduino code :slight_smile:

1 Like

I believe I have easily revised it to only report “Moving” every 250ms , by adding an If statement for the print command. if (TravelRemaining % 250 ==0){ Serial.print(2);}

trying to sort out the ramp on the motor speed but i think i’ll use the same if statement as part of it. Not sure how crucial it is as i only run the motor at half speed now. inrush is more important than turning off, son will focus on that. also i sized the motor controller to be more than big enough for this motor.

1 Like

That is good news. Have fun coding :slight_smile:

1 Like

I’m making good progress. The Cover status is displayed properly, and cover runs as expected using the forms app, the only issue now, is if i use the local pushbuttons, the sketch gets locked out and the cover status is stuck on Moving. What did you see in my sketch relative to reading the switch button states? Should i implement a debounce, or something else i have missed?