I have the following integer array [1,2,3,4,5,6,7,8,9]
I need to send it to Arduino UNO.
I've tried to send it as array of byte but it doesn't work.
C# code:
using System.IO.Ports;
private void Form1_Load(object sender, EventArgs e)
{serialPort1.Open();}
byte[] OUT = { 1,2,3,4,5,6,7,8,9 };
serialPort1.Write(OUT, 0, 12);
arduino code
void setup() {
Serial.begin(9600);
for (byte aPin = start; aPin <= 13 ; aPin++) {
pinMode(aPin, OUTPUT);}
for (byte apin = start; apin <= 13 ; apin++) {
digitalWrite(apin,HIGH);}
}
void loop(){
//Serial communication step
//Serial.write("6564456464646");
if(Serial.available()){
for(char j=0;j<Serial.available();j++)
{input[j]=Serial.read();}
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 AGAING
{
public partial class Form1 : Form
{
// int[] OUT = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // The array must be send by AUNO the resend to it.
byte[] OUT = new byte[12];
byte lamp = 0; // The index of lamp type array, and the indecation of the lamp type in the OUT array ti the AUNO.
string[] lampType = { "ENERGY SAVER", "TUNGSTEN", "FLUORESCENT", "METAL HALIDE" }; // Array for lamp type identification.
int sample; // The index of the report matrix, and giving number of test we did.
int[] endsample = new int [12]; //index array for saving the ended sample date.
int reportlenth = 3; // lenth of reporting arrays
string[] smplNo = new string[3]; //Array for sample number reporting.
string[] smpltype = new string[3]; // Array for sample lamp type reporting.
string[] startdate = new string[3]; // Array for sample starting date reporting.
string[] enddate = new string[3]; // Array for sample ending date reporting.
public Form1()
{
InitializeComponent();
// getportnames();
//serialPort1.Open();
}
// void getportnames()
//{
// string[] port = SerialPort.GetPortNames();
//PortcomboBox1.Items.AddRange(port);
// }
void warning(string warning) // Worning message properties.
{
MessageBox.Show(warning, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
private void Form1_Load(object sender, EventArgs e)
{
serialPort1.Open();
}
private void Form1_FormClosing(Object sender, FormClosingEventArgs e)
{
serialPort1.Close();
}
private void lamptype_SelectedIndexChanged(object sender, EventArgs e) // Projection the combo box of lamp type to the OUT Array
{
if (lamptype.SelectedIndex == 0)
{ lamp = 1; }
else if (lamptype.SelectedIndex == 1)
{ lamp = 2; }
else if (lamptype.SelectedIndex == 2)
{ lamp = 3; }
else if (lamptype.SelectedIndex == 3)
{ lamp = 4; }
else if (lamptype.SelectedIndex == -1)
{ lamp = 0; }
}
private void out1_CheckedChanged_1(object sender, EventArgs e)
{
if (out1.Checked)
{// If check box selected change its properties
out1.Font = new Font(out1.Font, FontStyle.Bold);
//Change text box properties.
sampleno1.Clear();
sampleno1.Enabled = true;
//Change text box properties.
enddate1.Clear();
datetime1.Clear();
lamptype1.Clear();
}
else {// unless reset the check box
out1.Font = new Font(out1.Font, FontStyle.Regular);
//Change text box properties.
sampleno1.Enabled = false;
// Reset the lamp type selection.
lamptype.SelectedIndex = -1;
lamptype.Text = "__Select lamp type__";
}
//
serialPort1.Write(OUT, 0, 12);
}
private void run1_Click_1(object sender, EventArgs e)
{//Check if any data are not marked give wornings.
if (out1.Checked == false) { warning("Select output"); }
else if (lamptype.SelectedIndex < 0) { warning("Select Lamp Type"); }
else if (String.IsNullOrEmpty(sampleno1.Text)) { warning("Enter sample number"); }
else if (sampleno1.TextLength != 10) { warning("Enter sample number correctly"); }
else
{// If yes change check box properties
out1.Enabled = false;
// Change run button properties
run1.Text = "In Use";
run1.Enabled = false;
run1.BackColor = Color.LightGreen;
run1.Font = new Font(run1.Font, FontStyle.Bold);
// Change text box properties
sampleno1.Enabled = false;
// Change stop button properties
stop1.Enabled = true;
stop1.BackColor = Color.OrangeRed;
stop1.Font = new Font(stop1.Font, FontStyle.Bold);
// Marking lamp type na d aoth data in text bos and reset lamp type combo box
lamptype1.Text = lampType[lamp - 1];
lamptype.Text = "__Select lamp type__";
// Show the starting date.
datetime1.Text = DateTime.Now.ToString("dd/mm/yyyy, hh:mm:ss");
// Save the sample data.
smplNo[sample] = sampleno1.Text; // Save the sample number.
smpltype[sample] = lamptype1.Text; // Save the lamp type date.
startdate[sample] = datetime1.Text; // Save the starting date.
// Save the reporting index for the ending date.
endsample[0] = sample;
// Increase the reporting index to give the sample counter and show it.
sample++;
MessageBox.Show("This is test no. " + sample);
// Worning to reset the saving data arraies. the reset it.
if (sample == reportlenth - 1)
{ warning("Only 1 more sample and the reporting system will reset, please inform the responsible"); }
else if (sample == reportlenth)
{ warning("The reporting system will reset, please inform the responsible");
sample = 0; }
//******************************************************************************//
// Set the output lamp type in the OUT array for AUNO.
OUT[0] = lamp;
}
//
//serialPort1.WriteLine("$");
serialPort1.Write(OUT, 0, 12);
}
You can use this part for c# part:
Connect();
if (serialPort1.IsOpen)
{
int MyInt = ToInt32(lblFirstNumber.Text);
byte[] b = GetBytes(MyInt);
serialPort1.Write(b, 0, 1);
int MyInt2 = ToInt32(txtFirstNumber.Text);
byte[] z = GetBytes(MyInt2);
serialPort1.Write(z, 0, 1);
int MyInt3 = ToInt32(txtSecondNumber.Text);
byte[] p = GetBytes(MyInt3);
serialPort1.Write(p, 0, 1);
serialPort1.Close();
}
else
{
MessageBox.Show("Please control your connection");
}
Related
I'm a teacher with a very limited programming background struggling with C# to make some videos for my students who are pretty much anxious and depressed during the pandemic. I'm using GTA5 as my platform because it is easy to customize, has amazing locations, hundreds of potential characters, and supports voice as well (I'm using Amazon Polly). Needlessly to say I'm stripping out all the violence and swearing.
The roadblock I'm hitting, and there isn't any support to speak of on any GTA forums or Mods forums, is how to display lines of text on the screen on demand. I've managed to do this hardcoded, but I would prefer to read this from a text file and display line by line, not by a timer, but on demand with a single key binding, ideally with the ability to go back and forth (but line by line).
A friend of mine who works for a AAA gaming company, doesn't know the GTA5 environment but said the solution would be to read the lines into an array. Unfortunately I don't have that programming knowledge, so I'm stuck at this point. Here is my code. Right now it will only display the last line of a test text file. Code is put together from Microsoft documentation and random GTA forum posts. Again, I can do this manually through multiple hardcoded lines of text, each with a key bind, but that's totally impractical. Need text file, one keybinding and a way go line by line (and ideally backwards)
using System;
using System.Drawing;
using System.Windows.Forms;
using GTA;
using GTA.Math;
using GTA.Native;
namespace TextDrawing
{
public class TextDraw : Script
{
public static string TextString { get; set; }
public TextDraw()
{
Tick += onTick;
Interval = 0;
KeyDown += Basics_KeyDown;
}
private void onTick(object sender, EventArgs e)
{
if (Game.IsLoading) return;
DrawText();
}
private void DrawText()
{
var pos = new Point(100, 100);
// TextString = "Default Start Screen goes here if you want it on load";
var Text4Screen = new GTA.UI.TextElement(TextString, pos, 0.35f, Color.White, GTA.UI.Font.ChaletLondon, GTA.UI.Alignment.Left, true, false, 1000); //last parameter is wrap width
Text4Screen.Enabled = true;
Text4Screen.Draw();
}
private void Basics_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.NumPad7) //code replace code in Basics_Tick
{
string[] lines = System.IO.File.ReadAllLines
(#"C:\Users\UserName\test.txt");
foreach (string line in lines)
{
TextString = line; //"Lesson 1 Topics Today. \n\n Part 1. Drawing Text on Screen \n\n Part 2. Customizations" + TextString2;
DrawText();
}
}
}
}
}
There are many ways to do this. But a few key code changes should make it work.
Create two class-level variables, an array to keep all the read files and an int index to keep track of your current index.
Create a function to read and invoke that in the constructor (ideally it should not be read in the constructor but rather triggered by external user action. But let's keep it simple for now).
In the key press event, update the string and increase the index. Your code is currently not working because you are iterating through the entire array and assigning text to a single variable. So only the last value is remains on screen.
public class TextDraw : Script
{
public static string TextString { get; set; }
public string[] AllLines { get; set; }
public int currentIndex = 0;
public TextDraw()
{
Tick += onTick;
Interval = 0;
KeyDown += Basics_KeyDown;
ReadAllLines(); ///Ideally not here. But should work still.
}
private void ReadAllLines()
{
AllLines = System.IO.File.ReadAllLines (#"C:\Users\UserName\test.txt");
}
private void onTick(object sender, EventArgs e)
{
if (Game.IsLoading) return;
DrawText();
}
private void DrawText()
{
var pos = new Point(100, 100);
// TextString = "Default Start Screen goes here if you want it on load";
var Text4Screen = new GTA.UI.TextElement(TextString, pos, 0.35f, Color.White, GTA.UI.Font.ChaletLondon, GTA.UI.Alignment.Left, true, false, 1000); //last parameter is wrap width
Text4Screen.Enabled = true;
Text4Screen.Draw();
}
private void Basics_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.NumPad7) //code replace code in Basics_Tick
{
if (currentIndex >= 0 && currentIndex < AllLines.Length)
{
TextString = AllLines[currentIndex];
}
else
{
TextString = "The End";
}
currentIndex++;
DrawText();
}
}
}
I'm not familiar with GTA scripting, but maybe create a member variable: int index = 0; Then use that to figure out which line to print in DrawText():
namespace TextDrawing
{
public class TextDraw : Script
{
// The lines of the text file
string [] lines;
// The index of the next line to print
int index = 0;
public TextDraw()
{
Tick += onTick;
Interval = 0;
KeyDown += Basics_KeyDown;
// Read the file here
lines = System.IO.File.ReadAllLines(#"C:\Users\UserName\test.txt");
}
private void onTick(object sender, EventArgs e)
{
if (0 == lines.Length || Game.IsLoading) return;
DrawText();
}
private void DrawText()
{
var pos = new Point(100, 100);
// Get next line
string textString = lines[index];
var Text4Screen = new GTA.UI.TextElement(textString, pos, 0.35f, Color.White, GTA.UI.Font.ChaletLondon, GTA.UI.Alignment.Left, true, false, 1000);
Text4Screen.Enabled = true;
Text4Screen.Draw();
}
private void Basics_KeyDown(object sender, KeyEventArgs e)
{
// When Keys.NumPad7 is pressed, show next line
if (e.KeyCode == Keys.NumPad7)
{
if (index < lines.Length - 1) index++;
}
// When Keys.NumPad8 is pressed, show previous line
else if (e.KeyCode == Keys.NumPad8)
{
if (index > 0) index--;
}
}
}
}
Updated to use onTick and go backwards. Apparently the onTick is needed to continually redraw the text. Being unfamiliar with the GTA API, I did not realize that.
I'm trying to code a Simon Says game and I ran into a problem. I cannot figure out how to make my Randomizer timer (that randomizes which boxes are lit up) to run by waves. I want it to run once, then when it's referred to again - run twice and so on.
This is RandomPlace():
private PictureBox RandomPlace()
{
PictureBox p = new PictureBox();
Random rnd = new Random();
switch (rnd.Next(1, 5))
{
case 1:
p = TopRight;
break;
case 2:
p = TopLeft;
break;
case 3:
p = BottomRight;
break;
case 4:
p = BottomLeft;
break;
}
return p;
} //Gets a random PictureBox
This is RandomImage():
private void RandomImage()
{
TopLeft.Enabled = false;
TopRight.Enabled = false;
BottomLeft.Enabled = false;
BottomRight.Enabled = false;
PictureBox a = RandomPlace();
if (a == TopLeft)
{
TopLeft.Image = Resources.TopLeftLit;
label1.Text = "TopLeft";
Thread.Sleep(500);
TopLeft.Image = Resources.TopLeft;
label2.Text = "TopLeftAFTERSLEEP";
Thread.Sleep(500);
pattern[patternRightNow] = 1;
patternRightNow++;
}
if (a == TopRight)
{
TopRight.Image = Resources.TopRightLit;
label1.Text = "TopRight";
Thread.Sleep(500);
TopRight.Image = Resources.TopRight;
label2.Text = "TopRightAFTERSLEEP"; //FIGURE OUT HOW TO RESET PICTURE
Thread.Sleep(500);
pattern[patternRightNow] = 2;
patternRightNow++;
}
if (a == BottomLeft)
{
this.BottomLeft.Image = Resources.BottomLeftLit;
label1.Text = "BottomLeft";
Thread.Sleep(500);
this.BottomLeft.Image = Resources.BottomLeft;
label2.Text = "BottomLeftAFTERSLEEP";
Thread.Sleep(500);
pattern[patternRightNow] = 3;
patternRightNow++;
}
if (a == BottomRight)
{
this.BottomRight.Image = Resources.BottomRightLit;
label1.Text = "BottomRight";
Thread.Sleep(500);
this.BottomRight.Image = Resources.BottomRight;
label2.Text = "BottomRightAFTERSLEEP";
Thread.Sleep(500);
pattern[patternRightNow] = 4;
patternRightNow++;
}
} //Lits up the random PictureBoxes and sets them back to normal
This is Randomizer_Tick():
rivate void Randomizer_Tick(object sender, EventArgs e)
{
RandomImage();
patternRightNow = 0;
tickCount++;
Randomizer.Stop();
ClickCheck();
} //Use RandomImage() to lit up random PictureBoxes on 5 waves, wave 1 - 1 PictureBox, wave 2 - 2 PictureBoxes and so on.
This is ClickCheck():
private void ClickCheck()
{
TopLeft.Enabled = true;
TopRight.Enabled = true;
BottomLeft.Enabled = true;
BottomRight.Enabled = true;
if (tickCount == clickCount)
{
CheckIfWin();
Randomizer.Start();
}
} //Enables the PictureBoxes to be pressed, after the user input reaches the current wave's amount of PictureBoxes lit up, disable PictureBoxes again and start the randomizer
Instead of using timers, I advise you to look at Tasks. It's much easier to create such statemachines.
Fill a list with colors and play it. Wait until the user pressed enough buttons and check the results.
For example: (haven't tested it, it's just for example/pseudo code)
It might contain some typo's.
private List<int> _sequence = new List<int>();
private List<int> _userInput = new List<int>();
private Random _rnd = new Random(DataTime.UtcNow.Milliseconds);
private bool _running = true;
private bool _inputEnabled = false;
private TaskCompletionSource<object> _userInputReady;
public async void ButtonStart_Click(object sender, EventArgs e)
{
if(_running)
return;
while(_running)
{
// add a color/button
_sequence.Add(_rnd.Next(4));
// play the sequence
for(int color in _sequence)
{
// light-up your image, whatever
Console.WriteLine(color);
// wait here...
await Task.Delay(300);
// turn-off your image, whatever
}
// clear userinput
_userInput.Clear();
_userInputReady = new TaskCompletionSource<object>();
_inputEnabled = true;
// wait for user input.
await _userInputReady.Task;
// compare the user input to the sequence.
for(int i=0;i<_sequence.Count;i++)
{
if(_sequence[i] != _userInput[i])
{
_running = false;
break;
}
}
}
Console.WriteLine("Not correct");
}
// one handler for all buttons. Use the .Tag property to store 0, 1, 2, 3
public void ButtonClick(object sender, EventArgs e)
{
if(!_inputEnabled)
return;
var button = (Button)sender;
// add user input to the queue
_userInput.Add((int)button.Tag);
if(_userInput.Count >= _sequence.Count)
_userInputReady.SetResult(null);
}
I have questions in my program that are jumbled up words and the user tries to un-jumble them. I want them to do this by them clicking multiple picture which have certain letters on it to form the word but I don't know how to connect the picture I have to a certain letter (e.g. Say I have a picture of the letter "a" how do I connect it so that the program knows when that picture is pressed the letter a is pressed).
This is the coding I have so far (to randomize the question and link the answers to it).
*Also I'm very new to coding so any suggestions please can you show what exactly to add/change to my code.
public partial class Level1 : Form
{
Random rnd = new Random(); //sets the random variable
List<string> strStrings = new List<string>() { "ZUZB", "HXAO", "MXAE", "KYCU", "CWEH", "PHIC", "HOCP", "SXIA", "ISHF", "KOJE" };//displays wprds om a list
Dictionary<string, string> dictStrings = new Dictionary<string, string>() //dictionary containing the word as the key, and the answer as the value in the key/value pair.
{
{ "ZUZB", "BUZZ" },
{ "HXAO", "HOAX" },
{ "MXAE", "EXAM" },
{ "KYCU", "YUCK" },
{ "CWEH", "CHEW" },
{ "PHIC", "CHIP" },
{ "HOCP", "CHOP" },
{ "SXIA", "AXIS" },
{ "ISHF", "FISH" },
{"KOJE", "JOKE" }
};
public Level1()
{
InitializeComponent();
}
int skip; //declares the skip variable
int score; //declares the score variable
int question; //decalres the question variable
private void nextButton_Click(object sender, EventArgs e)
{
if (strStrings.Count > 0)
{
string rndWord = strStrings[rnd.Next(0, strStrings.Count())];
lbljumble.Text = rndWord;
strStrings.Remove(rndWord);
}
else
{
lbljumble.Text = "No more questions!";
}
answerLabel.Text = ""; //randomly displays questions in the label until there are no more questions left to ask
score += 10; //add 10 to score and display in label
lblscore.Text = Convert.ToString(score);
question += 1; //add one to question number and display in label
lblqnum.Text = Convert.ToString(question);
tmrtime.Interval = (tmrtime.Interval) - 100; //amount of time taken after each question
}
private void answerButton_Click(object sender, EventArgs e)
{
string answer = (dictStrings.TryGetValue(lbljumble.Text, out answer)) ? answer : "";
answerLabel.Text = answer; //displays answer in label after the answer button is pressed to display the corresponding answer to the question
}
private void btnskip_Click(object sender, EventArgs e)
{
skip = 1; //skip equals 1
if (skip == 1) //of skip equals one
{
skip--; //take one from skip
lblskip.Text = " remaining: no"; //display that no skips are available
}
else
{
lblskip.Text = "No skips remaining"; //display no skips remaining
}
}
}
}
If I understand your question correctly you would like to know what letter was selected/clicked by the user.
You can dynamically create picture boxes, assign the .Name of the picture box to the value you would like the picture box to have then subscribe to the Click event for each picture box.
In the click event check the name of the sending picture box object sender. (you could alternatively use the pictureBx.Tag if you don't want to use the pictureBx.Name )
Here is an example form.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
lblAnswer.Text = "";
DrawLeters();
}
string checkAnswer = "check";
void DrawLeters()
{
this.SuspendLayout();
string[] alphabet = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z".Split(",");
var pictureLocation = new Point(0, 0);
for (int i = 0; i < alphabet.Length; i++)
{
//Create a picture box
PictureBox pictureBx = new PictureBox();
//set the default location of the box (0,0) in this scenario
pictureBx.Location = pictureLocation;
//make the box 16 by 16
pictureBx.Size = new Size(16, 16);
//set the name of the box to that of the letter it represents
pictureBx.Name = alphabet[i];
//assign a click event to the box
pictureBx.Click += PictureBx_Click;
//now create the image that will fill the box
Image img = new Bitmap(16, 16);
using (Graphics graph = Graphics.FromImage(img))
{
graph.Clear(Color.White);
Brush textBrush = new SolidBrush(Color.Black);
graph.DrawString(pictureBx.Name, this.Font, textBrush, 0, 0);
graph.Save();
}
//assign the image to the box
pictureBx.Image = img;
//add the box to the form
this.Controls.Add(pictureBx);
//change the location for the next box
pictureLocation.X += 17;
if (i % 10 == 0 && i > 0)
{
pictureLocation.Y += 17;
pictureLocation.X = 0;
}
}
this.ResumeLayout(false);
this.PerformLayout();
}
private void PictureBx_Click(object sender, EventArgs e)
{ // assign the clicked value to the answer label
if (sender is PictureBox pbx)
lblAnswer.Text += pbx.Name;
}
private void checkAnswerBtn_Click(object sender, EventArgs e)
{
//check the answer
if (lblAnswer.Text == checkAnswer)
lblFeedback.Text = "Correct!!";
else
lblFeedback.Text = "NO!!";
}
private void clearBtn_Click(object sender, EventArgs e)
{
//clear the answer label
lblAnswer.Text = "";
}
}
and the result is this.
i'm just checking that the answer is check you would use your own logic to determine the correct answer.
You can add some picture-boxes and name them as Letter_a_pb, Letter_b_pb etc., create a Click event, and then create an if statement to check which pictureBox is clicked and if it equal to the letter 'a'.
For instance:
string question = "a";
private void Letter_a_pb_Click(object sender, EventArgs e)
{
if (question == "a")
MessageBox.Show("Excellent!");
else
MessageBox.Show("Try Again!");
}
Let me know if this is what you've searched for.
I need to add PictureBox's (pictureBox11 to pictureBox30) to an array.
So instead of adding PictureBox's like this:
PictureBox[] Coins = new PictureBox[20];
Coins[0] = pictureBox11;
...
Coins[19] = pictureBox30;
I wrote a for cycle like this (DOESN'T WORK) :
for (int i = 11; i < 31; i++)
{
for (int j = 0; j < Coins.Length; j++)
{
Coins[j] = (PictureBox)Controls.Find(
"pictureBox" + i.ToString(), true)[0];
}
}
There might be a small stupid mistake somewhere because I use the same cycle for another thing and it works, idk, maybe I'm just blind and cant see the mistake.
Maybe it is relevant, so I will include the code I am assigning for the array elements:
for (int i = 0; i < Coins.Length; i++)
{
if (player.Bounds.IntersectsWith(Coins[i].Bounds))
{
Coins[i].Visible = false;
}
}
EVERYTHING works fine if I add them as shown in first code, but it is not very practical.
Why isn't the second code (the for cycle) I wrote working for me?
Is there a better way to add multiple pictureboxes to an array?
I am guessing you are making this more complicated than it has to be. To try and understand better, I assume that when you say I need to add pictureboxes (pictureBox11 to pictureBox30) to an array. that you mean there are 30+ PictureBoxs on your form and each PictureBox uses a naming convention such that each is named “pictureBoxX” where “X” is 1,2,3…30,31. Then you want to get a (consecutive?) group of “PictureBoxes” on the form to make invisible. I hope this is correct.
To simply make the picture boxes invisible, I do not think an array is needed. Simply loop through the picture boxes and make it invisible if the name matches a string of the form “pictureBoxX “. I used IndexsAreValid method to validate the start and end indexes. It is also used in the code for an array implementation below this code.
Make PictureBoxes invisible without an array
private void SetPictureBoxesInvisible(int start, int end) {
int size = -1;
string targetString = "";
if (IndexsAreValid(start, end, out size)) {
for (int i = start; i < end + 1; i++) {
try {
targetString = "pictureBox" + i;
PictureBox target = (PictureBox)Controls.Find(targetString, true)[0];
if (target != null) {
target.Visible = false;
}
}
catch (IndexOutOfRangeException e) {
return;
}
}
}
}
If you must have a PictureBox array returned, then the code below should work.
First, to get an array of PictureBoxs as you want you need an array to store them. But first you need to know how big to make it. From your posted code it appears that you want to get picture boxes 11-30 and put them in an array. So we can get the size from these numbers… i.e. 30-11=19 +1 = 20. That’s about all you need. Simply create the array and loop through all the picture boxes and grab pictureBox11-pictureBox30. When done we can use this array to make these `PictureBoxes” invisible.
I created a method IsValidPic similar to a tryParse to validate if the given index (1,2,3..30) is valid. If it is out of range, I simply ignore that value. This gives you the ability to grab an individual picture box in case the desired picture boxes are not contiguous. I used a few buttons to test the methods.
Hope this helps.
private PictureBox[] GetPictureBoxes(int start, int end) {
int size = - 1;
if (IndexsAreValid(start, end, out size)) {
PictureBox curPic = null;
PictureBox[] allPics = new PictureBox[size];
int index = 0;
for (int i = start; i <= end; i++) {
if (IsValidPic(i, out curPic)) {
allPics[index] = curPic;
index++;
}
}
return allPics;
}
else {
return new PictureBox[0];
}
}
private Boolean IndexsAreValid(int start, int end, out int size) {
if (start < 1 || end < 1) {
size = -1;
return false;
}
if (start > end) {
size = -1;
return false;
}
size = end - start + 1;
return true;
}
private Boolean IsValidPic(int index, out PictureBox picture) {
string targetName = "pictureBox" + index;
try {
PictureBox target = (PictureBox)Controls.Find(targetName, true)[0];
if (target != null) {
picture = target;
return true;
}
picture = null;
return false;
}
catch (IndexOutOfRangeException e) {
picture = null;
return false;
}
}
private void ResetAll() {
foreach (PictureBox pb in this.Controls.OfType<PictureBox>()) {
pb.Visible = true;
}
}
private void button1_Click(object sender, EventArgs e) {
TurnInvisible(2, 3);
}
private void button3_Click(object sender, EventArgs e) {
TurnInvisible(11, 30);
}
private void button4_Click(object sender, EventArgs e) {
TurnInvisible(1,7);
}
private void TurnInvisible(int start, int end) {
PictureBox[] pictureBoxesToChange = GetPictureBoxes(start, end);
foreach (PictureBox pb in pictureBoxesToChange) {
if (pb != null)
pb.Visible = false;
}
}
private void button2_Click(object sender, EventArgs e) {
ResetAll();
}
My purpose in below program is getting 16 bytes of data from microcontroller and processing data for appropriate instructions. There are a lot of related questions and answers here but I couldnt find anything about in below issue. I can get 16 bytes from MCU. Values of bytes are correct and I can see them in dataGridView but the sequence of bytes is changing . For example at first MCUData[0] = 0x01 , MCUData[1] = 0xFE , MCUData[2] = 0xCC then it changes to MCUData[0] = 0xFE , MCUData[1] = 0xCC , MCUData[2] = 0x01. İt is like some problem shifting my datas in byte array. I am sure my MCU is sending data correctly because I checked in one of serial terminal program. My code is in below
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.Threading;
namespace SerialCommunicationMCU
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
dataGridView1.Columns.Add("MCUData", "Byte Name");
dataGridView1.Columns.Add("MCUData", "Byte Value");
}
public System.IO.Ports.SerialPort SerialPc;
#region Variables
public string AvailablePort;
public string[] Ports = SerialPort.GetPortNames();
byte[] MCUData = new byte[16];
#endregion
private void Connect_Click(object sender, EventArgs e)
{
DataGreedByteNameShow();
SerialConnectandRead();
ConnectButton.Enabled = false;
DisconnectButton.Enabled = true;
}
private void Disconnect_Click(object sender, EventArgs e)
{
SerialPc.Close();
ConnectButton.Enabled = true;
DisconnectButton.Enabled = false;
}
public void SerialConnectandRead()
{
SerialPc = new SerialPort(AvailablePort, 115200, Parity.None, 8, StopBits.One);
try
{
SerialPc.Open();
SerialPc.DataReceived += new SerialDataReceivedEventHandler(SerialPc_DataReceived);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Serial Port Error");
}
}
private void SerialPc_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPc.DiscardNull = false;
SerialPc.Read(MCUData, 0, 16);
SerialPc.ReceivedBytesThreshold = 16;
DataGreedByteValueShow();
}
private void Form1_Load(object sender, EventArgs e)
{
foreach (string port in Ports)
{
comboBox1.Items.Add(port);
}
DisconnectButton.Enabled = false;
}
public void DataGreedByteNameShow()
{
dataGridView1.Rows.Add("MCUData[0]");
dataGridView1.Rows.Add("MCUData[1]");
dataGridView1.Rows.Add("MCUData[2]");
dataGridView1.Rows.Add("MCUData[3]");
dataGridView1.Rows.Add("MCUData[4]");
dataGridView1.Rows.Add("MCUData[5]");
dataGridView1.Rows.Add("MCUData[6]");
dataGridView1.Rows.Add("MCUData[7]");
dataGridView1.Rows.Add("MCUData[8]");
dataGridView1.Rows.Add("MCUData[9]");
dataGridView1.Rows.Add("MCUData[10]");
dataGridView1.Rows.Add("MCUData[11]");
dataGridView1.Rows.Add("MCUData[12]");
dataGridView1.Rows.Add("MCUData[13]");
dataGridView1.Rows.Add("MCUData[14]");
dataGridView1.Rows.Add("MCUData[15]");
}
private void DataGreedByteValueShow()
{
dataGridView1.Rows[0].Cells[1].Value = MCUData[0];
dataGridView1.Rows[1].Cells[1].Value = MCUData[1];
dataGridView1.Rows[2].Cells[1].Value = MCUData[2];
dataGridView1.Rows[3].Cells[1].Value = MCUData[3];
dataGridView1.Rows[4].Cells[1].Value = MCUData[4];
dataGridView1.Rows[5].Cells[1].Value = MCUData[5];
dataGridView1.Rows[6].Cells[1].Value = MCUData[6];
dataGridView1.Rows[7].Cells[1].Value = MCUData[7];
dataGridView1.Rows[8].Cells[1].Value = MCUData[8];
dataGridView1.Rows[9].Cells[1].Value = MCUData[9];
dataGridView1.Rows[10].Cells[1].Value = MCUData[10];
dataGridView1.Rows[11].Cells[1].Value = MCUData[11];
dataGridView1.Rows[12].Cells[1].Value = MCUData[12];
dataGridView1.Rows[13].Cells[1].Value = MCUData[13];
dataGridView1.Rows[14].Cells[1].Value = MCUData[14];
dataGridView1.Rows[15].Cells[1].Value = MCUData[15];
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
AvailablePort = comboBox1.SelectedItem.ToString();
}
}
}
According to the MSDN documentation: http://msdn.microsoft.com/en-us/library/ms143549(v=vs.110).aspx
Reads a number of bytes from the SerialPort input buffer and writes
those bytes into a byte array at the specified offset.
public int Read(
byte[] buffer,
int offset,
int count
)
Here's how your current code uses the function:
SerialPc.Read(MCUData, 0, 16);
Your buffer is a global variable defined as:
byte[] MCUData = new byte[16];
This is one way that you can use to solve your problem:
List<byte> MCUDataOverTime = new List<byte>();
private void SerialPc_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPc.DiscardNull = false;
SerialPc.Read(MCUData, 0, 16);
MCUDataOverTime.AddRange(MCUData);
SerialPc.ReceivedBytesThreshold = 16;
DataGreedByteValueShow();
}
private void DataGreedByteValueShow()
{
if (MCUDataOverTime.Count >= 16)
{
dataGridView1.Rows[0].Cells[1].Value = MCUDataOverTime[0];
dataGridView1.Rows[1].Cells[1].Value = MCUDataOverTime[1];
dataGridView1.Rows[2].Cells[1].Value = MCUDataOverTime[2];
dataGridView1.Rows[3].Cells[1].Value = MCUDataOverTime[3];
dataGridView1.Rows[4].Cells[1].Value = MCUDataOverTime[4];
dataGridView1.Rows[5].Cells[1].Value = MCUDataOverTime[5];
dataGridView1.Rows[6].Cells[1].Value = MCUDataOverTime[6];
dataGridView1.Rows[7].Cells[1].Value = MCUDataOverTime[7];
dataGridView1.Rows[8].Cells[1].Value = MCUDataOverTime[8];
dataGridView1.Rows[9].Cells[1].Value = MCUDataOverTime[9];
dataGridView1.Rows[10].Cells[1].Value = MCUDataOverTime[10];
dataGridView1.Rows[11].Cells[1].Value = MCUDataOverTime[11];
dataGridView1.Rows[12].Cells[1].Value = MCUDataOverTime[12];
dataGridView1.Rows[13].Cells[1].Value = MCUDataOverTime[13];
dataGridView1.Rows[14].Cells[1].Value = MCUDataOverTime[14];
dataGridView1.Rows[15].Cells[1].Value = MCUDataOverTime[15];
}
}
The code above would solve the problem where your values change over time. It's happening in the first place because every time SerialPc_DataReceived() is called, you've set it so that every "Read" will store its result in your MCUData byte array. Consequently, it will end up overwriting your MCUData array since you've hardcoded the start-from offset to 0.
That's why it looks like your data is being shifted. You have to understand, fundamentally, these streams don't conveniently store data for you in the way where an index of 0 means the beginning of time (when the stream first opened and started). If you want it to work that way, you'll have to store it yourself.
Please note that the code above does not attempt to empty the serial input buffer. You're pulling 16 bytes every time you do a read when the DataReceived is called, but I wonder if the MCU is sending a lot more than that with every DataReceived message.
Edit:
List<byte> MCUDataOverTime = new List<byte>();
private void SerialPc_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPc.DiscardNull = false;
int readcount = 0;
byte [] temp;
do
{
readcount = SerialPc.Read(MCUData, 0, 16);
if (readcount > 0)
{
temp = new byte[readcount];
Array.Copy(MCUData, 0, temp, 0, readcount);
MCUDataOverTime.AddRange(temp);
SerialPc.ReceivedBytesThreshold = 16;
DataGreedByteValueShow();
}
} while (readcount > 0);
}
private void DataGreedByteValueShow()
{
if (MCUDataOverTime.Count >= 16)
{
dataGridView1.Rows[0].Cells[1].Value = MCUDataOverTime[0];
dataGridView1.Rows[1].Cells[1].Value = MCUDataOverTime[1];
dataGridView1.Rows[2].Cells[1].Value = MCUDataOverTime[2];
dataGridView1.Rows[3].Cells[1].Value = MCUDataOverTime[3];
dataGridView1.Rows[4].Cells[1].Value = MCUDataOverTime[4];
dataGridView1.Rows[5].Cells[1].Value = MCUDataOverTime[5];
dataGridView1.Rows[6].Cells[1].Value = MCUDataOverTime[6];
dataGridView1.Rows[7].Cells[1].Value = MCUDataOverTime[7];
dataGridView1.Rows[8].Cells[1].Value = MCUDataOverTime[8];
dataGridView1.Rows[9].Cells[1].Value = MCUDataOverTime[9];
dataGridView1.Rows[10].Cells[1].Value = MCUDataOverTime[10];
dataGridView1.Rows[11].Cells[1].Value = MCUDataOverTime[11];
dataGridView1.Rows[12].Cells[1].Value = MCUDataOverTime[12];
dataGridView1.Rows[13].Cells[1].Value = MCUDataOverTime[13];
dataGridView1.Rows[14].Cells[1].Value = MCUDataOverTime[14];
dataGridView1.Rows[15].Cells[1].Value = MCUDataOverTime[15];
}
}
I'd increase the size of MCUData to 1024 bytes or more since 16 bytes seems kind of small. The next thing that you'd need to do is keep track of the start and end points of each frame. I'm guessing that it'll always be 16 in the case of your microcontroller but in general you should have the microcontroller throwing out null values or a special string of symbols to indicate the end of a message.