Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. recieveing and splitting serial data from Arduino in C#

recieveing and splitting serial data from Arduino in C#

Scheduled Pinned Locked Moved C#
csharpcssdatabaselinqgraphics
11 Posts 3 Posters 6 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    auting82
    wrote on last edited by
    #1

    Hi, I am fairly new to C# coding so be gentle :laugh: I am trying to receive measurement data from a two sensors DHT11 and soimoisture sensor connected to my Arduino. At this stage I just want to receive the raw measurement data and represent them in separate text boxes. I have almost managed it but I am getting some errors. More specifically I get this error: System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index' Here is my code:

    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 System.IO.Ports;

    namespace PlantMonitoringApp
    {
    public partial class Form1 : Form
    {

        private SerialPort myport;
        private DateTime datetime;
        private string in\_data;
    
        public Form1()
        {
            InitializeComponent();
        }
    
        private void start\_btn\_Click(object sender, EventArgs e)
        {
            myport = new SerialPort();
            myport.BaudRate = 9600;
            myport.PortName = port\_name\_tb.Text;
            myport.Parity = Parity.None;
            myport.DataBits = 8;
            myport.StopBits = StopBits.One;
            myport.DataReceived += Myport\_DataReceived1;
            try
            {
                myport.Open();
                time\_text\_box.Text = "";
    
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error");
            }
    
            // timer1.Start();
    
    
        }
        void Myport\_DataReceived1(object sender, SerialDataReceivedEventArgs e)
        {
    
            in\_data = myport.ReadLine();
    
            this.Invoke(new EventHandler(displaydata\_event));
            /\*String dataFromArduino = myport.ReadLine();
            String\[\] dataTempHumidMoisture = dataFromArduino.Split();
            int Temperature = (int)(Math.Round(Convert.ToDecimal(dataTempHumidMoisture\[0\]), 0));
            //int Humidity = (int)(Math.Round(Convert.ToDecimal(dataTempHumidMoisture\[1\]), 0));
           // int SoilMoisture= (int)(Math.Round(Convert.ToDecimal(dataTempHumidMoisture\[2\]), 0));
            txtTemperature.Text = Temperature.ToString() + "C";
           // txtHumidity.Text = Humidity.ToString() + "%";
            //txtHumidity.Text = SoilMoistu
    
    L L 2 Replies Last reply
    0
    • A auting82

      Hi, I am fairly new to C# coding so be gentle :laugh: I am trying to receive measurement data from a two sensors DHT11 and soimoisture sensor connected to my Arduino. At this stage I just want to receive the raw measurement data and represent them in separate text boxes. I have almost managed it but I am getting some errors. More specifically I get this error: System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index' Here is my code:

      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 System.IO.Ports;

      namespace PlantMonitoringApp
      {
      public partial class Form1 : Form
      {

          private SerialPort myport;
          private DateTime datetime;
          private string in\_data;
      
          public Form1()
          {
              InitializeComponent();
          }
      
          private void start\_btn\_Click(object sender, EventArgs e)
          {
              myport = new SerialPort();
              myport.BaudRate = 9600;
              myport.PortName = port\_name\_tb.Text;
              myport.Parity = Parity.None;
              myport.DataBits = 8;
              myport.StopBits = StopBits.One;
              myport.DataReceived += Myport\_DataReceived1;
              try
              {
                  myport.Open();
                  time\_text\_box.Text = "";
      
              }
              catch (Exception ex)
              {
                  MessageBox.Show(ex.Message, "Error");
              }
      
              // timer1.Start();
      
      
          }
          void Myport\_DataReceived1(object sender, SerialDataReceivedEventArgs e)
          {
      
              in\_data = myport.ReadLine();
      
              this.Invoke(new EventHandler(displaydata\_event));
              /\*String dataFromArduino = myport.ReadLine();
              String\[\] dataTempHumidMoisture = dataFromArduino.Split();
              int Temperature = (int)(Math.Round(Convert.ToDecimal(dataTempHumidMoisture\[0\]), 0));
              //int Humidity = (int)(Math.Round(Convert.ToDecimal(dataTempHumidMoisture\[1\]), 0));
             // int SoilMoisture= (int)(Math.Round(Convert.ToDecimal(dataTempHumidMoisture\[2\]), 0));
              txtTemperature.Text = Temperature.ToString() + "C";
             // txtHumidity.Text = Humidity.ToString() + "%";
              //txtHumidity.Text = SoilMoistu
      
      L Offline
      L Offline
      Lost User
      wrote on last edited by
      #2

      There's no guarantee that tokens has data.

      txtTemperature.Text = tokens[0];

      "(I) am amazed to see myself here rather than there ... now rather than then". ― Blaise Pascal

      A 1 Reply Last reply
      0
      • L Lost User

        There's no guarantee that tokens has data.

        txtTemperature.Text = tokens[0];

        "(I) am amazed to see myself here rather than there ... now rather than then". ― Blaise Pascal

        A Offline
        A Offline
        auting82
        wrote on last edited by
        #3

        Why is there no guarantee? Sometimes it works, that's the strange part . Any suggestions? :confused:

        L 1 Reply Last reply
        0
        • A auting82

          Hi, I am fairly new to C# coding so be gentle :laugh: I am trying to receive measurement data from a two sensors DHT11 and soimoisture sensor connected to my Arduino. At this stage I just want to receive the raw measurement data and represent them in separate text boxes. I have almost managed it but I am getting some errors. More specifically I get this error: System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index' Here is my code:

          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 System.IO.Ports;

          namespace PlantMonitoringApp
          {
          public partial class Form1 : Form
          {

              private SerialPort myport;
              private DateTime datetime;
              private string in\_data;
          
              public Form1()
              {
                  InitializeComponent();
              }
          
              private void start\_btn\_Click(object sender, EventArgs e)
              {
                  myport = new SerialPort();
                  myport.BaudRate = 9600;
                  myport.PortName = port\_name\_tb.Text;
                  myport.Parity = Parity.None;
                  myport.DataBits = 8;
                  myport.StopBits = StopBits.One;
                  myport.DataReceived += Myport\_DataReceived1;
                  try
                  {
                      myport.Open();
                      time\_text\_box.Text = "";
          
                  }
                  catch (Exception ex)
                  {
                      MessageBox.Show(ex.Message, "Error");
                  }
          
                  // timer1.Start();
          
          
              }
              void Myport\_DataReceived1(object sender, SerialDataReceivedEventArgs e)
              {
          
                  in\_data = myport.ReadLine();
          
                  this.Invoke(new EventHandler(displaydata\_event));
                  /\*String dataFromArduino = myport.ReadLine();
                  String\[\] dataTempHumidMoisture = dataFromArduino.Split();
                  int Temperature = (int)(Math.Round(Convert.ToDecimal(dataTempHumidMoisture\[0\]), 0));
                  //int Humidity = (int)(Math.Round(Convert.ToDecimal(dataTempHumidMoisture\[1\]), 0));
                 // int SoilMoisture= (int)(Math.Round(Convert.ToDecimal(dataTempHumidMoisture\[2\]), 0));
                  txtTemperature.Text = Temperature.ToString() + "C";
                 // txtHumidity.Text = Humidity.ToString() + "%";
                  //txtHumidity.Text = SoilMoistu
          
          L Offline
          L Offline
          Luc Pattyn
          wrote on last edited by
          #4

          Hi, Reading from a SerialPort isn't always easy. Your code may fail to work reliably for several reasons: 1. The SerialPort.DataReceived event is fired when one or more characters have been received; there is no strict definition of when it fires. And it is not synchronized to anything, it is not aware of what you consider a message (such as a piece of text terminated by a newline). 2. SerialPort.ReadLine() returns one line of text assuming a newline has been received; if not a timeout exception will occur. 3. You are using two threads, and communicating between them using a single string (in_data). It is conceivable that two DataReceived events occur before the other thread manages to execute displaydata_event(). In that case, you would loose data. If you are in charge of software on both sides, the simplest approach would be like this: 1. don't let the peripheral send data at will, make it a slave that doesn't have the initiative, instead it executes commands when it receives them 2. send a command to the peripheral requesting (a measurement and) the return of data 3. wait a short time 4. read the data synchronously So basically that would be:

          SerialPort port;
          ...
          port.Open();
          ...
          port.WriteLine("T"); // the command to measure and send a temperature
          Thread.Sleep(100); // wait for measurement and transmission
          string temp=port.ReadLine(); // receive the result
          textbox.Text=temp;

          The delay should be sufficient for the peripheral to perform the measurement, and the serial port to transmit and receive the data. Since 9600 Baud would correspond to approx 1 msec per character, delaying for 100 msec seems reasonable, and not too harmful for your GUI interface. Of course, the proper way to do communication is asynchronous, but then you need to take care of proper synchronisation and data transfer; that would include: 1. reading the port with ReadExisting, not ReadLine; 2. collecting the incoming characters; 3. identifying message boundaries; 4. passing results to another thread, using some real data structure (possibly a queue). Hope this helps. :)

          Luc Pattyn [My Articles] Nil Volentibus Arduum

          A 1 Reply Last reply
          0
          • A auting82

            Why is there no guarantee? Sometimes it works, that's the strange part . Any suggestions? :confused:

            L Offline
            L Offline
            Lost User
            wrote on last edited by
            #5

            I always tied my serial port "Read()", and the "bytes read" that it returns, with the "BytesToRead" as indicated by the port. In other words, I never have occasion to just "read" without first requesting data via a "write". The reading is managed within the timeout "window". Write ... wait (check the time) ... (expecting bytes to be read > 0 ) read ... more bytes to be read? ... wait (timeout?) .... read again ... etc. until all read or timeout; then the next write for data request. You build a message until no more bytes to be read; then fire it off to the rest of the processing; insuring you actually split 3 tokens and didn't receive garbage in the first place.

            "(I) am amazed to see myself here rather than there ... now rather than then". ― Blaise Pascal

            1 Reply Last reply
            0
            • L Luc Pattyn

              Hi, Reading from a SerialPort isn't always easy. Your code may fail to work reliably for several reasons: 1. The SerialPort.DataReceived event is fired when one or more characters have been received; there is no strict definition of when it fires. And it is not synchronized to anything, it is not aware of what you consider a message (such as a piece of text terminated by a newline). 2. SerialPort.ReadLine() returns one line of text assuming a newline has been received; if not a timeout exception will occur. 3. You are using two threads, and communicating between them using a single string (in_data). It is conceivable that two DataReceived events occur before the other thread manages to execute displaydata_event(). In that case, you would loose data. If you are in charge of software on both sides, the simplest approach would be like this: 1. don't let the peripheral send data at will, make it a slave that doesn't have the initiative, instead it executes commands when it receives them 2. send a command to the peripheral requesting (a measurement and) the return of data 3. wait a short time 4. read the data synchronously So basically that would be:

              SerialPort port;
              ...
              port.Open();
              ...
              port.WriteLine("T"); // the command to measure and send a temperature
              Thread.Sleep(100); // wait for measurement and transmission
              string temp=port.ReadLine(); // receive the result
              textbox.Text=temp;

              The delay should be sufficient for the peripheral to perform the measurement, and the serial port to transmit and receive the data. Since 9600 Baud would correspond to approx 1 msec per character, delaying for 100 msec seems reasonable, and not too harmful for your GUI interface. Of course, the proper way to do communication is asynchronous, but then you need to take care of proper synchronisation and data transfer; that would include: 1. reading the port with ReadExisting, not ReadLine; 2. collecting the incoming characters; 3. identifying message boundaries; 4. passing results to another thread, using some real data structure (possibly a queue). Hope this helps. :)

              Luc Pattyn [My Articles] Nil Volentibus Arduum

              A Offline
              A Offline
              auting82
              wrote on last edited by
              #6

              Well you just made me realize how much I don't know :doh: . Where specifically in my code can I put this delay?

              Thread.Sleep(100);

              I really appreciate your effort ,but it would be way more helpful if you could comment on where in my code I can implement this?

              L 1 Reply Last reply
              0
              • A auting82

                Well you just made me realize how much I don't know :doh: . Where specifically in my code can I put this delay?

                Thread.Sleep(100);

                I really appreciate your effort ,but it would be way more helpful if you could comment on where in my code I can implement this?

                L Offline
                L Offline
                Luc Pattyn
                wrote on last edited by
                #7

                Hi, I've thrown out most all of your code! My code example replaces everything (it still needs port initialization). Under the assumptions I made (master-slave operation, PC sends command then peripheral replies), there is no need for events, secondary threads, Invoke, etc. If you can't turn your peripheral into a slave, which would mean it can still send results whenever it likes, then you need more of your code, however even then a delay could save your day: put it anywhere between the start of the DataReceived event and the line that holds SerialPort.ReadLine; this would guarantee (actually increase the probability) that an entire message up to and including newline is present in the serial buffer when ReadLine gets called. However now the delay also increases the probability that you would loose a message, so it makes sense to opt for a shorter delay (I would at first not go below 20 msec though). :)

                Luc Pattyn [My Articles] Nil Volentibus Arduum

                A 1 Reply Last reply
                0
                • L Luc Pattyn

                  Hi, I've thrown out most all of your code! My code example replaces everything (it still needs port initialization). Under the assumptions I made (master-slave operation, PC sends command then peripheral replies), there is no need for events, secondary threads, Invoke, etc. If you can't turn your peripheral into a slave, which would mean it can still send results whenever it likes, then you need more of your code, however even then a delay could save your day: put it anywhere between the start of the DataReceived event and the line that holds SerialPort.ReadLine; this would guarantee (actually increase the probability) that an entire message up to and including newline is present in the serial buffer when ReadLine gets called. However now the delay also increases the probability that you would loose a message, so it makes sense to opt for a shorter delay (I would at first not go below 20 msec though). :)

                  Luc Pattyn [My Articles] Nil Volentibus Arduum

                  A Offline
                  A Offline
                  auting82
                  wrote on last edited by
                  #8

                  I have solved this problem and the code is as follows:

                  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 System.IO.Ports;
                  using System.Diagnostics;

                  namespace PlantMonitoringApp
                  {
                  public partial class Form1 : Form
                  {

                      public SerialPort myport;
                      private DateTime datetime;
                       string in\_data;
                  
                      public Form1()
                      {
                          Sensor newSensor;
                          newSensor = new Sensor();
                          InitializeComponent();
                      }
                  
                      private void start\_btn\_Click(object sender, EventArgs e)
                      {
                  
                          
                          myport = new SerialPort();
                          myport.BaudRate = 9600;
                          myport.PortName = "COM3";//port\_name\_tb.Text;
                          myport.Parity = Parity.None;
                          myport.DataBits = 8;
                          myport.StopBits = StopBits.One;
                          myport.DataReceived += Myport\_DataReceived1;
                  
                          try
                          {
                              myport.Open();
                              time\_text\_box.Text = "";
                              
                          }
                          catch (Exception ex)
                          {
                              MessageBox.Show(ex.Message, "Error");
                          }
                          
                  
                          time\_text\_box.Text = "";
                  
                      }
                      void Myport\_DataReceived1(object sender, SerialDataReceivedEventArgs e)
                      {
                  
                          in\_data = myport.ReadLine();
                  
                          this.Invoke(new EventHandler(displaydata\_event));
                          
                      }
                      private void displaydata\_event(object sender, EventArgs e)
                      {
                          datetime = DateTime.Now;
                          string time = datetime.Hour + ":" + datetime.Minute + ":" + datetime.Second;
                          time\_text\_box.Text = time;
                  
                          string\[\] sensorData = in\_data.Split(new char\[\] { ' ', ' ' });
                          List tokens = new List();
                         // int tmp1 = int.Parse(tokens\[0\]);
                          try
                          {
                              
                              foreach (string s in sensorData)
                              {
                                  if (s.Length != 0)
                                  {
                                      tokens.Add(s);
                                  }
                              }
                  
                  
                              txtTemperature.Text = tokens\[0\];
                              txtHumidity.Text = tokens\[1\];
                              txtSoil\_moisture.Text = tokens\[2\];
                  
                             // int tmp1 = int.Parse(tokens\[0\]);
                  
                  L 1 Reply Last reply
                  0
                  • A auting82

                    I have solved this problem and the code is as follows:

                    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 System.IO.Ports;
                    using System.Diagnostics;

                    namespace PlantMonitoringApp
                    {
                    public partial class Form1 : Form
                    {

                        public SerialPort myport;
                        private DateTime datetime;
                         string in\_data;
                    
                        public Form1()
                        {
                            Sensor newSensor;
                            newSensor = new Sensor();
                            InitializeComponent();
                        }
                    
                        private void start\_btn\_Click(object sender, EventArgs e)
                        {
                    
                            
                            myport = new SerialPort();
                            myport.BaudRate = 9600;
                            myport.PortName = "COM3";//port\_name\_tb.Text;
                            myport.Parity = Parity.None;
                            myport.DataBits = 8;
                            myport.StopBits = StopBits.One;
                            myport.DataReceived += Myport\_DataReceived1;
                    
                            try
                            {
                                myport.Open();
                                time\_text\_box.Text = "";
                                
                            }
                            catch (Exception ex)
                            {
                                MessageBox.Show(ex.Message, "Error");
                            }
                            
                    
                            time\_text\_box.Text = "";
                    
                        }
                        void Myport\_DataReceived1(object sender, SerialDataReceivedEventArgs e)
                        {
                    
                            in\_data = myport.ReadLine();
                    
                            this.Invoke(new EventHandler(displaydata\_event));
                            
                        }
                        private void displaydata\_event(object sender, EventArgs e)
                        {
                            datetime = DateTime.Now;
                            string time = datetime.Hour + ":" + datetime.Minute + ":" + datetime.Second;
                            time\_text\_box.Text = time;
                    
                            string\[\] sensorData = in\_data.Split(new char\[\] { ' ', ' ' });
                            List tokens = new List();
                           // int tmp1 = int.Parse(tokens\[0\]);
                            try
                            {
                                
                                foreach (string s in sensorData)
                                {
                                    if (s.Length != 0)
                                    {
                                        tokens.Add(s);
                                    }
                                }
                    
                    
                                txtTemperature.Text = tokens\[0\];
                                txtHumidity.Text = tokens\[1\];
                                txtSoil\_moisture.Text = tokens\[2\];
                    
                               // int tmp1 = int.Parse(tokens\[0\]);
                    
                    L Offline
                    L Offline
                    Luc Pattyn
                    wrote on last edited by
                    #9

                    Hi, I see no problems; create a Sensor class that has: - a private SerialPort object; - three private numeric results (temp, moist, soilMoist); could be int, float, double,...; - a constructor taking the parameters you may need (e.g. port name) and creating the serial port; this is also where you should wire the DataReceived event, so it executes once, not for every start! - a public Start() method that does what your start button does now; you should remove the DataReceived wiring here. - a public Stop() method that does what your stop button does now; - a DataReceived event handler that does what your current one does, except it interprets the incoming string, turns the fields into numbers using float.Parse() or float.TryParse() or something similar, and stuffs the results in the numeric class fields temp, moist, soilMoist; saving results in simple numeric variables does not violate thread safety, there will be no need to use any Invoke. - three public properties Temp, Moist, SoilMoist that offer a getter to read those results; - a public Dispose() method that calls SerialPort.Dispose(), something you have omitted so far. As a result, a Sensor instance would collect whatever you receive on the serial port from calling Start() till calling Stop() or Dispose(). And reading the result properties would immediately return the most recent value that is available (and zero if no results yet). In order to see the results in your current Form's TextBoxes, you could use a System.Windows.Forms.Timer that periodically (say once every second) gets the results and moves them into the TextBoxes, using float.ToString(). Of course you then would call Start() and Stop/Dispose() only once. Possible improvement: you could also add a private DateTime measured and a public property DateTime Measured, which will offer the DateTime when the data was last successfully updated. PS: you can format the current time simply with DateTime.Now.ToString("HH:mm:ss"); :)

                    Luc Pattyn [My Articles] Nil Volentibus Arduum

                    A 1 Reply Last reply
                    0
                    • L Luc Pattyn

                      Hi, I see no problems; create a Sensor class that has: - a private SerialPort object; - three private numeric results (temp, moist, soilMoist); could be int, float, double,...; - a constructor taking the parameters you may need (e.g. port name) and creating the serial port; this is also where you should wire the DataReceived event, so it executes once, not for every start! - a public Start() method that does what your start button does now; you should remove the DataReceived wiring here. - a public Stop() method that does what your stop button does now; - a DataReceived event handler that does what your current one does, except it interprets the incoming string, turns the fields into numbers using float.Parse() or float.TryParse() or something similar, and stuffs the results in the numeric class fields temp, moist, soilMoist; saving results in simple numeric variables does not violate thread safety, there will be no need to use any Invoke. - three public properties Temp, Moist, SoilMoist that offer a getter to read those results; - a public Dispose() method that calls SerialPort.Dispose(), something you have omitted so far. As a result, a Sensor instance would collect whatever you receive on the serial port from calling Start() till calling Stop() or Dispose(). And reading the result properties would immediately return the most recent value that is available (and zero if no results yet). In order to see the results in your current Form's TextBoxes, you could use a System.Windows.Forms.Timer that periodically (say once every second) gets the results and moves them into the TextBoxes, using float.ToString(). Of course you then would call Start() and Stop/Dispose() only once. Possible improvement: you could also add a private DateTime measured and a public property DateTime Measured, which will offer the DateTime when the data was last successfully updated. PS: you can format the current time simply with DateTime.Now.ToString("HH:mm:ss"); :)

                      Luc Pattyn [My Articles] Nil Volentibus Arduum

                      A Offline
                      A Offline
                      auting82
                      wrote on last edited by
                      #10

                      Wow, that's a lot of things :sigh: Have no clue how to transform that into code :confused:. Thanks for the reply anyways

                      L 1 Reply Last reply
                      0
                      • A auting82

                        Wow, that's a lot of things :sigh: Have no clue how to transform that into code :confused:. Thanks for the reply anyways

                        L Offline
                        L Offline
                        Luc Pattyn
                        wrote on last edited by
                        #11

                        Quote:

                        Have no clue how to transform that into code

                        I suggest you get yourself a book on C# and study that. It will teach you the language, the relevant library classes, how the create your own classes, how to work with objects, etc. I haven't seen it myself, lots of people recommend the e-book .NET Book Zero by Charles Petzold[^]. Personally I prefer a dead tree edition where you can easily navigate and annotate. And once you get to know some of the fundamentals, start looking at other people's code, there are plenty of excellent articles here at CodeProject. :)

                        Luc Pattyn [My Articles] Nil Volentibus Arduum

                        1 Reply Last reply
                        0
                        Reply
                        • Reply as topic
                        Log in to reply
                        • Oldest to Newest
                        • Newest to Oldest
                        • Most Votes


                        • Login

                        • Don't have an account? Register

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • World
                        • Users
                        • Groups