Controlling two servos via pc using mouse arduino - c#

I want to control two servos via PC, using a mouse x-y coordinates. x-y coordinates of the cursor is sent to the serial port:
For the arduino part, I am getting 6 character string and dividing it into 2 parts. Then these parts are converted into integer value and send to arduino pins to set servo position:
#include <Servo.h>
String readString, servo1, servo2;
Servo myservo1; // create servo object to control a servo
Servo myservo2;
void setup() {
Serial.begin(9600);
myservo1.attach(6); //the pin for the servo control
myservo2.attach(7);
}
void loop() {}
void serialEvent() {
while (Serial.available()) {
delay(2);
if (Serial.available() >0) {
char c = Serial.read(); //gets one byte from serial buffer
readString += c; //makes the string readString
}
}
if (readString.length() >0) {
Serial.println(readString); //see what was received
// expect a string like 07002100 containing the two servo positions
servo1 = readString.substring(0, 3); //get the first three characters
servo2 = readString.substring(3, 6); //get the next three characters
Serial.println(servo1); //print ot serial monitor to see results
Serial.println(servo2);
int n1; //declare as number
int n2;
char carray1[6]; //magic needed to convert string to a number
servo1.toCharArray(carray1, sizeof(carray1));
n1 = atoi(carray1);
char carray2[6];
servo2.toCharArray(carray2, sizeof(carray2));
n2 = atoi(carray2);
myservo1.write(n1); //set servo position
myservo2.write(n2);
readString="";
}
}
However, the code is very slow. I need to move the mouse very slowly to make servos move. Instant movement from let's say from 50 degree to 170 takes 1 second to move the servo. Could you offer a better option to control two servos in this case?
Controlling only one servo works very well, and it moves servo instantly without any lags:
#include <Servo.h>
Servo x;
int xval;
void setup() {
Serial.begin(9600);
x.attach(9);
}
void loop() {
}
void serialEvent() {
xval = Serial.parseInt();
if(xval!=0) {
x.write(xval);
}
}
Code in C#:
public partial class Form1 : Form
{
SerialPort port;
public Form1()
{
InitializeComponent();
init();
}
private void init()
{
port = new SerialPort();
port.PortName = "COM1";
port.BaudRate = 9600;
//porty = new SerialPort();
try
{
port.Open();
}
catch (Exception e1)
{
MessageBox.Show(e1.Message);
}
}
int x = 0, y = 0;
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
Graphics g = CreateGraphics();
Pen p = new Pen(Color.Navy);
Pen erase = new Pen(Color.White);
x = e.X; y = e.Y;
double curWidth = this.Width / 180;
double x2 = Math.Round(x / curWidth);
double curHeight = this.Height / 180;
double y2 = Math.Round(y / curHeight);
label1.Text = x2.ToString(); label2.Text = y2.ToString();
string valx = x2.ToString();
string valy = y2.ToString();
while(valx.Length < 3)
{
valx = '0' + valx;
}
while(valy.Length < 3)
{
valy = '0' + valy;
}
string valsum = valx+valy;
label3.Text = valsum.ToString();
if (port.IsOpen)
{
port.WriteLine(valsum);
}
}
}
In the above code I am taking x and y coordinates and converting it to approx 180 range. After I join the values into one string to send it through serial port.

In order to improve the performance of your code you can remove the delay and use the maximum Baud Rate (115200). For the sake of simplicity I compacted your Arduino code below:
#include <Servo.h>
// create 2 servos x and y
Servo x;
Servo y;
void setup() {
Serial.begin(115200);
x.attach(6);
y.attach(7);
}
void loop() {
//empty
}
void serialEvent() {
//set servo position.
if (Serial.available()>0) {
//Receives 2 angles separated per a comma
x.write(Serial.parseInt()); //Returns first angle
y.write(Serial.parseInt()); //Returns second angle
Serial.read(); //Read the '\n'. I am not sure if it is necessary.
}
}
Increase the Baud Rate also in the C# source code:
port.BaudRate = 115200;
In addition, I suggest you send the angles separated by a comma through the serial. So, instead of:
while(valx.Length < 3) {
valx = '0' + valx;
}
while(valy.Length < 3) {
valy = '0' + valy;
}
string valsum = valx+valy;
label3.Text = valsum.ToString();
if (port.IsOpen) {
port.WriteLine(valsum);
}
Replace with:
if (port.IsOpen) {
port.WriteLine(valx + ',' + valy); //Note that WriteLine appends a '\n' character in the end.
}

Related

Emgu.CV Object Marker

I built a pan-tilt system with 2 servos and laser diode. The system was controlled by Arduino Nano. By using Emgu.CV in C#, I created a grayscale image and convert it to a black-white image using GrayImg.ThresholdBinaryInv. I'm using Inverse Kinematics calculations but the laser diode couldn't mark the object well. Where did I make a mistake? Image coordinate system and servos coordinate systems are not same. Thus, I did mapping and scaling. It should work but the accuracy of the laser diode is inaccurate.
Arduino Code
#include <Servo.h>
int Th1, Th2, tmp;
Servo M1;
Servo M2;
void setup()
{
Serial.begin(9600);
pinMode(6,OUTPUT; // Laser Diode
digitalWrite(6,0);
Th1 = 0;
Th2 = 0;
M1.attach(3);
M1.write(90);
M2.attach(9);
M2.write(90);
}
void loop()
{
delay(200); //sync issue only
if(Serial.available()>=2)
{
Th1 = Serial.read(); //read only one byte
Th2 = Serial.read();
M1.write(Th1);
M2.write(Th2);
//Remove any extra worng reading
while(Serial.available()) tmp = Serial.read();
// Run the robotic arm here. For testing, we will
digitalWrite(6,1);
delay(500);
digitalWrite(6,0);
delay(500);
//switch On or switch off a LED according to Th1 value
//Serial.print('1'); // This tell the PC that Arduino is Ready for next angles
}
}
C# Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;
namespace ObjectMarker
{
public partial class Form1 : Form
{
private Capture capture;
private Image<Bgr, Byte> IMG;
private Image<Gray,Byte> blackWhite;
private Image<Gray, Byte> GrayImg;
private int N, Xpx, Ypx; // N -> Number of non-black pixels
private double Xcm, Ycm;
private double myScale;
private double Th1, Th2;
static SerialPort _serialPort;
public byte []Buff = new byte[1];
public Form1() //Constructor of the Form1
{
InitializeComponent();
//myScale = 65.0 / 480.0;
myScale = 1.7 / 13;
_serialPort = new SerialPort();
_serialPort.PortName = "COM4";//Set your board COM
_serialPort.BaudRate = 9600;
_serialPort.Open();
}
private void processFrame(object sender, EventArgs e) // Most important function
{ //You're not connected any camera - null
if (capture == null)//very important to handel excption - capture - point of the camera
{
try
{
capture = new Capture(1); //creatine a object
}
catch (NullReferenceException excpt)
{
MessageBox.Show(excpt.Message);
}
}
IMG = capture.QueryFrame();// capture the current frame. Get an image.
GrayImg = IMG.Convert<Gray, Byte>();
blackWhite = GrayImg.ThresholdBinaryInv(new Gray(25),new Gray (255));
Xpx = 0;
Ypx = 0;
N = 0;
for (int i = 0; i < blackWhite.Width; i++) {
for (int j = 0; j < blackWhite.Height; j++) {
if(blackWhite[j,i].Intensity > 128){
N++;
Xpx += i;
Ypx += j;
}
}
}
if(N>0){
Xpx = Xpx / N;
Ypx = Ypx / N;
Xpx = (blackWhite.Width / 2) - Xpx; //320 - xpx
Ypx = Ypx - (blackWhite.Height / 2); // ypx - 240
Xcm = Xpx * myScale;
Ycm = Ypx * myScale;
double d3 = 28; // Laser to wall dist. (?)
double Zcm = 108; // Cam to wall dist.
double d1 = 3.50; // Joint to joint dist.
double l2 = 4.50; // Joint to laser dist. (?)
textBox1.Text = Xcm.ToString();
textBox2.Text = Ycm.ToString();
textBox3.Text = N.ToString();
textBox11.Text = Zcm.ToString();
textBox12.Text = myScale.ToString();
//Inverse Calculations
double Px, Py, Pz,Diff = 0;
// Mapping
//Px = Zcm;
Px = -1 * Zcm;
Py = -1 * Xcm;
Pz = Ycm + Diff; // The laptop has built-in camera and the pan-tilt above the keyboard so there's a distance between camera and pan-tilt system (laser diode)
textBox8.Text = Px.ToString();
textBox9.Text = Py.ToString();
textBox10.Text = Pz.ToString();
Th1 = Math.Atan((Py / Px));
Th2 = Math.Atan((Math.Sin(Th1) * (Pz - d1)) / Py);
//Th1 = Math.Atan(Ycm / Xcm);
//Th2 = Math.Atan((Math.Sin(Th1) * (Zcm - d1)) / Ycm);
textBox4.Text = Th1.ToString();
textBox5.Text = Th2.ToString();
Th1 = (Th1 * (180 / Math.PI));
Th2 = (Th2 * (180 / Math.PI));
Th1 += 90;
Th2 += 90;
textBox6.Text = Th1.ToString();
textBox7.Text = Th2.ToString();
label11.Text = trackBar1.Value.ToString();
label12.Text = trackBar2.Value.ToString();
Buff[0] = (byte)trackBar1.Value; //Th1
Buff[2] = (byte)trackBar2.Value; //Th2
_serialPort.Write(Buff,0,2);
}
else
{
textBox1.Text = "";
textBox2.Text = "";
textBox3.Text = N.ToString();
Buff[0] = (byte)90; //Th1
Buff[2] = (byte)90; //Th2
_serialPort.Write(Buff, 0, 2);
}
try
{
imageBox1.Image = IMG;
imageBox2.Image = GrayImg;
imageBox3.Image = blackWhite;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void button1_Click(object sender, EventArgs e)
{
//Application.Idle += processFrame; //Serial comm between arduino and computer
timer1.Enabled = true;
button1.Enabled = false;
button2.Enabled = true;
}
private void button2_Click(object sender, EventArgs e)
{
//Application.Idle -= processFrame;
timer1.Enabled = false;
button1.Enabled = true;
button2.Enabled = false;
}
private void button3_Click(object sender, EventArgs e) // I measure the object size from paint. It gave me pixel size. Then I measure the real size of the image. Real size divided by pixel size gives scale.
{
IMG.Save("G:\\CurrentFrame" + ".jpg");
}
void Timer1Tick(object sender, EventArgs e)
{
processFrame(sender,e);
}
}
}
EDIT: Images has been added. The problem is if I change the tape location inaccuracy of the system isn't accurate. In some points laser marked the tape very well but mostly on corners there's huge difference between laser and tape.
https://i.stack.imgur.com/fmm1x.png
https://i.stack.imgur.com/Quadr.png
https://i.stack.imgur.com/9SWa4.png
This question is very hard to answer, as there are many places where an error could have crept in. Without further diagnosis and knowledge of the system, it's almost impossible to give a clear answer. I suggest you perform some more diagnosis, and if the answer still eludes you, reformulate your question (or close it when you find the answer yourself).
Some possible sources of error (and points for diagnosis) could be:
In the Arduino code, you throw away anything after the first two bytes of Serial communication. Why would there be extra bytes? Are you sure the extra bytes are garbage, and, inversely, the first two bytes are the correct ones?
In the C# mapping of coordinates, you perform a translation of the coordinates. Are you sure the system is exactly axis-parallel? An error of just a few degrees quickly adds up. You might need to add a rotational transformation.
Your C# code returns an angle (in degrees) for the servo. This could be negative, or above 255, so converting it to a byte could, on overflow, lead to passing a (completely) wrong value to the Arduino.

Reading to newline from serial port returning first value

I'm trying to read data from a serial port that I have an Arduino and joystick connected to.
When trying to print out the received data to check if I'm getting it, it continues to print out the same value it was at when it connected.
I am sending data from my serial port in this format: Xaxis:yAxis:switchBool
Heres my C# code in a WPF application
public partial class MainWindow : Window {
public Timer loopTimer;
string comport = "COM7";
SerialPort sp;
public MainWindow() {
InitializeComponent();
SetTimer();
OpenPort();
}
private void SetTimer() {
//Setup the timer
loopTimer = new Timer(500);
// Hook up the Elapsed event for the timer.
loopTimer.Elapsed += TryRead;
loopTimer.AutoReset = true;
loopTimer.Enabled = false;
}
public void OpenPort() {
sp = new SerialPort();
try {
string portName = comport;
sp.PortName = portName;
sp.BaudRate = 9600;
sp.Open();
Debug.WriteLine("Connected");
loopTimer.Enabled = true;
}
catch (Exception) {
MessageBox.Show("Please give a valid port number or check your connection");
loopTimer.Enabled = false;
}
}
public void TryRead(object sender, EventArgs e) {
string s = sp.ReadLine();
Debug.WriteLine(s);
Debug.WriteLine("-");
}
}
Here's my arduino code:
int xPin = A1;
int yPin = A0;
int swPin = A2;
float deadzone = .05;
void setup() {
Serial.begin(9600);
pinMode(swPin, INPUT);
}
void loop() {
float xVal = (((analogRead(xPin) + 1) / 1023.) * 2) -1;
if (xVal < deadzone && xVal > -deadzone ) {
xVal = 0;
}
float yVal = (((analogRead(yPin) + 1) / 1023.) * 2) -1;
if (yVal < deadzone && yVal > -deadzone ) {
yVal = 0;
}
int swVal = analogRead(swPin);
bool switchDown;
if (swVal == 0) {
switchDown = true;
} else {
switchDown = false;
}
Serial.println( String(xVal) + ":" + String(yVal) + ":" + switchDown);
}
Here's an example of what the data should and does look like in the Arduino's serial monitor when moving the thumbstick around:
-1.00:0.70:0
-0.80:0.50:0
-0.70:0.60:0
Running my C# code above and not moving the thumbstick I only get 0.00:0.00:0.00 every time I read, and if I move it before starting I will only receive whatever value that was.
Instead of reading on an interval I would just read whenever new data arrives on the bus. below is an example of how to do that.
SerialPort Port= new SerialPort("Com7");
Port.DataReceived+=OnSerialRecieve;
private void OnSerialRecieve(object sender, SerialDataReceivedEventArgs e)
{
if ((sender as SerialPort).IsOpen)
{
string DataRecieved=(sender as SerialPort).ReadExisting();
}
}
from there you can split it up as you need to. otherwise if you know the exact amount of data your expecting you can sue SerialPort.ReadByte to read exactly the number of bytes you need.

Making a c# program go back to a point and carry on

I have a list of code that is implemented from a button click load event in one method. I also have a Mouse Event that returns the e.X and e.Y values. MY problem is that the program runs through the button click load event and I can see the mouse events but I do not know how to return the program to run it again from a position with the new e.X and e.Y values. I have tried to separate it into various methods but I cannot seem to be able to 'call' them or 'goto' them. Any advice on what I need to do? The code is listed below.
Many thanks Steve
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.Data.SqlClient;
using System.IO;
using System.Runtime.InteropServices;
namespace HPXSV8
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int curX;
private int curY;
//private double ll;
//private double lr;
//private double lt;
//private double lb;
//private double GPSLat;
//private double GPSLong;
public int allimages;
public void Form1_Load(object sender, EventArgs e)
{
}
public void btnLoadFiles_Click(object sender, EventArgs e)
{
//Loads the data file syvwname
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "Load Survey View Data File";
ofd.Filter = "Survey View File |*.syvw";
if (ofd.ShowDialog() == DialogResult.OK)
{
string syvwname = ofd.FileName;
string sfn = ofd.SafeFileName;
string[] loaddata = new string[500];
int allimages = 0;
string line;
StreamReader file = new StreamReader(syvwname);
List<string> list = new List<string>();
while ((line = file.ReadLine()) != null)
{
listBox1.Items.Add(line);
loaddata[allimages] = line;
allimages++;
}
string[,] gpsdata = new string[allimages, 3];
int z = 0;
do
{
//slices up the .syvw file into 3 string values comma delimited
string chop1 = loaddata[z];
string[] part = chop1.Split(',');
string[] parts = part.ToArray();
gpsdata[z, 0] = parts[0];
gpsdata[z, 1] = parts[1];
gpsdata[z, 2] = parts[2];
z++;
}
while (z < allimages);
//load jpg 0001 to picture box
int strlength = syvwname.Length;
int sfnamelength = sfn.Length;
string mainimage = gpsdata[0, 0];
string trimname = syvwname.Remove(strlength - sfnamelength, sfnamelength);
string filelocation = (trimname + #"\" + mainimage);
pictureBox1.ImageLocation = filelocation;
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
//Calculate values of lat and long to compare with image values
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double picboxX = 800.00; ////check these values in final program size
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//Calculate fraction position of cursor to picture
double curposX = curX / picboxX;
//+++++++++++++++++++++++++++++++++++++++++++++++++
label1.Text = "CurposX = " + curposX.ToString();
//+++++++++++++++++++++++++++++++++++++++++++++++++
//Calculate values of lat and long to compare with image values
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
double picboxY = 600.00;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//Calculate fraction position of cursor to picture
double curposY = curY / picboxY;
//+++++++++++++++++++++++++++++++++++++++++++++++++
label2.Text = "CurposY =" + curposY.ToString(); // Test Line
//+++++++++++++++++++++++++++++++++++++++++++++++++
/// Calculate GPSLongditude from mouse positions
double ll = Convert.ToDouble(gpsdata[2, 1]); //??????????????????????????????
double lr = Convert.ToDouble(gpsdata[2, 2]);
//GPS Postion calculations based on picture box mouse position
//Longitude
double GPSLong = ll + ((lr - ll) * curposX);
//+++++++++++++++++++++++++++++++++++++++++++++++++
label3.Text = "GpsLong =" + GPSLong.ToString(); /// Test Line
//+++++++++++++++++++++++++++++++++++++++++++++++++
// Calculate GPSLatitude from mouse positions
double lb = Convert.ToDouble(gpsdata[1, 2]);
double lt = Convert.ToDouble(gpsdata[1, 1]);
//GPS Postion calculations based on picture box mouse position
//Latitude
double GPSLat = lt + ((lb - lt) * curposY);
//Look for values in my_datatable that fall within +/- 0.0005 of GPS Lat and Long
// This may be made to be a variable to increase the number of images
//=================================================================================
//Look for values in gpsdata[]
int row = 3;
int chs = 0;
string[] chosenimage = new string[allimages]; // value if all images are selected
//=================================================================================
double area = 0.0005; //later make this a UI variable
//=================================================================================
do
{
//make 4 values +/- 0.0005 of gpsdata[]
if (GPSLat <= (Convert.ToDouble(gpsdata[row, 1]) + area))
{
if (GPSLat >= (Convert.ToDouble(gpsdata[row, 1]) - area))
{
if (GPSLong <= (Convert.ToDouble(gpsdata[row, 2]) + area))
{
if (GPSLong >= (Convert.ToDouble(gpsdata[row, 2]) - area))
{
chosenimage[chs] = gpsdata[row, 0];
chs++;
}
}
}
}
row++;
}
while (row < allimages);
//+++++++++++++++++++++++++++++++++++++++++++++++++
label4.Text = "GpsLat = " + GPSLat.ToString();
//+++++++++++++++++++++++++++++++++++++++++++++++++
while ((line = file.ReadLine()) != null)
{
listBox2.Items.Add(line);
chosenimage[chs] = line;
allimages++;
}
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
public void btnSelect_Click(object sender, EventArgs e)
{
// Use to select images to use for notes
//label1.Text = insertnote.Text;
}
public void insertnote_MouseDown(object sender, MouseEventArgs e)
{
//Use for entering text into notepad area
}
public void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
//Get location of mouse click on 001.jpg
int curX = e.X;
int curY = e.Y;
//+++++++++++++++++++++++++++++++++++++++++++++++++
label1.Text = "X = " + curY.ToString() + " ; Y =" + curY.ToString(); /// Test Line
//+++++++++++++++++++++++++++++++++++++++++++++++++
}
}
} // End of Namespace HPXSV8
I hope I'm understanding your question. You have two events. You want Event A to update the X and Y values and the other Event B to do some calculation based on those values. If Event A occurs during the calculation, you want to redo the calculation.
I'll have to check up on this later, but I'm sure that the events you wrote are blocking. Meaning Event A has to complete before it can proceed with Event B. Additionally, Event B has to complete before it can proceed with Event A. This is why it won't work.
You could try doing this with a worker thread. Event B signals the thread to perform calculations. Event A will tell the worker thread that a value is invalid and has to redo a calculation.

Sending Byte from Raspberry Pi to Arduino With I2c & IoT for PWM

I am very new to the using both I2C and C#/Windows IoT so apologies up front if any of this is a dumb question. I have a Raspberry Pi 3 master and Arduino slave. I am trying to send a value from a slider on my UI form over I2C to the Arduino which I will use to adjust my PWM duty cycle. There a couple of issues I am having and can't work out if its the Pi, Arduino or both.
Here is my Arduino Slave code:
#include <Wire.h>
#define MyAddress 0x03
byte ReceivedData;
int pass;
void setup() {
Wire.begin(MyAddress);
Wire.onReceive(I2CReceived);
Serial.begin(9600);
//Wire.onRequest(I2CRequest);
}
void loop() {
delay(100);
}
void I2CReceived(int NumberOfBytes)
{
/* WinIoT have sent data byte; read it */
byte ReceivedData = Wire.read();
Serial.println(ReceivedData);
if (ReceivedData <= 127){
Serial.println("Equal or under");
return;
}else{
Serial.println("over");
return;
}
}
And my Pi Master:
using System;
using Windows.Devices.Gpio;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media;
using Windows.UI.Core;
using Windows.Devices.Enumeration;
using Windows.Devices.I2c;
using System.Diagnostics;
using System.Threading;
namespace I2COutput
{
public sealed partial class MainPage : Page
{
private I2cDevice TransPump;
private Timer periodicTimer;
private const byte pump = 0x03;
double pos;
public MainPage()
{
InitializeComponent();
initcomunica();
}
private async void initcomunica()
{
var pumpset = new I2cConnectionSettings(pump);
pumpset.BusSpeed = I2cBusSpeed.StandardMode;
string aqs = I2cDevice.GetDeviceSelector("I2C1");
var dis = await DeviceInformation.FindAllAsync(aqs);
TransPump = await I2cDevice.FromIdAsync(dis[0].Id, pumpset);
}
private async void SendChange()
{
byte[] sendpos;
try
{
sendpos = BitConverter.GetBytes(pos);
TransPump.Write(sendpos);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}
private void tempLbl_SelectionChanged(object sender, RoutedEventArgs e)
{
}
private void slider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
pos = slider.Value;
temp2Lbl.Text = pos.ToString();
Convert.ToInt16(pos);
SendChange();
return;
}
}
}
The first issue I am having is that my ReceivedData on the Arduino is always 0 not matter what the value of sendpos is on the Pi (yes, it does change when I move the slider).
The second issue I am having is the first time the slider is moved I get the output on the Arduino serial but then nothing after. If I either reset or reload the Arduino I then get the output of the initial slider change again and nothing after.
Apologies if any of this is too vague or explained poorly, any help or nudge in the right direction would be greatly appreciated.
Thanks in advance.
you have to change the "Wire.onReceive(I2CReceived);" to the loop because when it's in the setup the arduino exute it only one, (sorry for my english)
I wrote an I2C slave for Arduino UNO based on Nick Gammon Web Site.
It worked but I could not get more than 10 K byte /second. You have some missing part in your own code.
Here is stripped down version of the Arduino Code
#include <Wire.h>
#define I2C_400K 1 // http://www.gammon.com.au/forum/?id=10896
bool receiveEventcommandReceived = false;
bool requestEventCommandReceived = false;
int _currentRequestInputParameterLen = 0;
void receiveEvent(int howMany) {
receiveEventcommandReceived = true;
while (Wire.available() > 0)
{
_cmd = Wire.read();
if (_cmd == ArduinoCommand_EpromWrite) {
// Some code
}
else if (_cmd == ArduinoCommand_EpromRead) {
_addr0 = Wire.read();
_addr1 = Wire.read();
_addr = (_addr0 * 256) + _addr1;
_len = Wire.read();
_EEPROMBuffer = NusbioEx.EEPROMRead(_addr, _len);
_r = 128+1;
}
else {
// Some code
}
_count++;
}
}
void requestEvent()
{
requestEventCommandReceived = true;
if (_cmd == ArduinoCommand_EpromRead) {
Wire.write(_EEPROMBuffer, strlen(_EEPROMBuffer));
}
else { // ArduinoCommand_EpromWrite or any other api
int v1 = _r >> 8;
int v2 = _r & 0xFF;
char buffer[2];
buffer[0] = v1;
buffer[1] = v2;
Wire.write(buffer, 2); // MUST BE SENT IN ONE BUFFER -> TO CREATE ONE I2C TRANSACTION
}
}
void SetupI2C() {
Wire.begin(I2C_SLAVE_ADDR); // join i2c bus with address #4
#if I2C_400K
TWBR = 12; // http://www.gammon.com.au/forum/?id=10896
#endif
Wire.onReceive(receiveEvent); // register event
Wire.onRequest(requestEvent); // register event
}
void setup() {
SetupI2C();
}
void loop() {
if (requestEventCommandReceived)
{
requestEventCommandReceived = false;
}
#endif
}

XAudio2 - Play generated sine, when changing frequency clicking sound

I want to develop an app to match your tinnitus frequency : A frequency is played and the user decrease or increase the freqency by pressing a plus or minus button. (see part of the codes, based on some coding from stackoverflow thx :-))
public static short[] BufferSamples = new short[44100 * 1 * 2];
private SourceVoice sourceVoice;
private AudioBuffer buffer;
private int Tfreq;
public MatchTinn()
{
InitializeComponent();
Loaded += MatchTinn_Loaded;
TFreq = 5000;
}
private void MatchTinn_Loaded(object sender, RoutedEventArgs e)
{
var dataStream = DataStream.Create(BufferSamples, true, true);
buffer = new AudioBuffer
{
LoopCount = AudioBuffer.LoopInfinite,
Stream = dataStream,
AudioBytes = (int)dataStream.Length,
Flags = BufferFlags.EndOfStream
};
FillBuffer(BufferSamples, 44100, Tfreq);
var waveFormat = new WaveFormat();
XAudio2 xaudio = new XAudio2();
MasteringVoice masteringVoice = new MasteringVoice(xaudio);
sourceVoice = new SourceVoice(xaudio, waveFormat, true);
// Submit the buffer
sourceVoice.SubmitSourceBuffer(buffer, null);
}
private void FillBuffer(short[] buffer, int sampleRate, int frequency)
{
if (sourceVoice != null)
{
sourceVoice.FlushSourceBuffers();
}
double totalTime = 0;
for (int i = 0; i < buffer.Length - 1; i += 2)
{
double time = (double)totalTime / (double)sampleRate;
short currentSample = (short)(Math.Sin(2 * Math.PI * frequency * time) * (double)short.MaxValue);
buffer[i] = currentSample;
buffer[i + 1] = currentSample;
totalTime++;
}
private void m1_OnTap(object sender, GestureEventArgs e)
{
Tfreq = Tfreq - 1;
if (Tfreq < 0)
{
Tfreq = 0;
}
FillBuffer(BufferSamples, 44100, Tfreq);
}
private void p1_OnTap(object sender, GestureEventArgs e)
{
Tfreq = Tfreq + 1;
if (Tfreq > 16000)
{
Tfreq = 16000;
}
FillBuffer(BufferSamples, 44100, Tfreq);
}
Playing the frequency is fine, but when the user presses a button you here a clicking sound when the frequency is updated. Do you have any idea what makes the sound and how i can get rid of it?
Thanks.
When you change the frequency, you're causing a discontinuity in the waveform that manifests as a click. Instead of making your signal calculations against absolute time, you should keep track of the phase of your sine calculation (e.g. a value from 0 to 2*pi), and figure out how much you need to add to your phase (subtracting 2*pi every time you exceed 2*pi) for the next sample when playing a specific frequency. This way, when you change frequency, the phase that you supply as a parameter to Math.Sin doesn't change abruptly causing a click.
Expanding on the answer #spender gave (I need 50 rep to add comment to his answer), I had a similar problem with naudio. I was able to solve the issue by adding two bool values that monitored the current sign of the sine value and the previous sign of the sine value. If the previous sine was negative and the current sine is positive, we know we can safely adjust the frequency of the sine wave.
double sine = amplitude * Math.Sin(Math.PI * 2 * frequency * time);
isPreviousSineWaveValPositive = isSineWaveValPositive;
if (sine < 0)
{
isSineWaveValPositive = false;
}
else
{
isSineWaveValPositive = true;
}
// When the time is right, change the frequency
if ( false == isPreviousSineWaveValPositive && true == isSineWaveValPositive )
{
time = 0.0;
frequency = newFrequency;
}
Here's an example how you can get rid of the clicking. Instead of using a time, you should keep track of the current phase and calculate how much the phase is changed on the required frequency. Also this _currentPhase must be persistent so it will have the previous value. (declaring it within the method would result in a click aswell (on most frequencies)
private double _currentPhase = 0;
private void FillBuffer(short[] buffer, int sampleRate, int frequency)
{
if (sourceVoice != null)
{
sourceVoice.FlushSourceBuffers();
}
var phaseStep = ((Math.PI * 2) / (double)sampleRate) * frequency;
for (int i = 0; i < buffer.Length - 1; i += 2)
{
_currentPhase += phaseStep;
short currentSample = (short)(Math.Sin(_currentPhase) * (double)short.MaxValue);
buffer[i] = currentSample;
buffer[i + 1] = currentSample;
}
}

Categories

Resources