I recently got my hands on an Arduino (Uno), and I was wondering something.
I've got no external volume changer for my speakers, so I thought, maybe hook up a potentiometer to the Arduino, and then use that to control the volume (in Windows). But is that possible?
To read a value of an Arduino pin using maybe Visual C, C++ (or some more 'multi-platform' language)? And then using that to set the volume level in Windows (and if it's possible also in Linux).
I thought it might be possible, because if you use:
Serial.println(analogRead([pin with potentiometer]));
You can get the values of the potentiometer to the pc (via USB). So is there any way to read those values in C or C++?
I know how to set the volume in Windows via C or C++, I only need to know if there is a way to read out the values of a potentiometer in a (Visual) C or C++ script.
Definitely. And using exactly the same method: serial communication. Since I'm not a great Windows expert, I can't write you a complete Windows example, but here are some snippets that may get you started on a Unix (Linux, OS X, etc.):
Code on the Arduino:
#define POT_PIN 1
void setup()
{
Serial.begin(9600); // 9600 baud is more than enough
}
void loop()
{
int pot = analogRead(POT_PIN);
unsigned char byte = (pot + 2) >> 2; // round and divide by 4
Serial.write(pot);
delay(100); // or do something useful
}
Code on the computer:
#include <termios.h>
struct termios tio;
memset(&tio, 0, sizeof(tio));
// Open serial port in mode `8N1', non-blocking
tio.c_cflag = CS8 | CREAD | CLOCAL;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 5;
int fd = open(device, O_RDONLY);
cfsetospeed(&tio, B9600);
cfsetispeed(&tio, B9600);
tcsetattr(fd, TCSANOW, &tio);
while (1) {
unsigned char byte;
read(fd, &byte, 1);
use_some_library_to_set_volume(byte);
}
There are some things to consider.
You want the PC to listen to your "potentiometer Arduino" only. You don't want it to listen to any random USB data coming at any port. There is a need to create a data protocol which is somewhat unique to your device. If you just spam analog readings you'll get some raw data like "123 128 133 145", which could mean anything and come from any kind of device.
When you type Serial.whatever, the Arduino is likely spitting out an UART signal. I don't know a thing about Arduino, but hopefully it has either RS-232 or USB circuitry on board. You will need a matching port on the PC, the traditional RS-232 9 pin d-sub connector is getting increasingly rare on modern computers. You might need a RS-232 to USB converter.
You need a mean to determine which COM port the device is connected to. USB ports and traditional RS-232 ports work the same, as far as the PC is concerned. Either let the user pick the port manually, or design a more intelligent algorithm to find the port where your device is connected.
Study the Windows API for all the serial port routines. Documentation and examples at MSDN.
Related
I hava an STM32 f401RE. I am using Mbed library for setting up a conexion from STM32 to PC. I want to send via serial a char sequence to the board. As an answer i expect a blinking LED. E.g: led1 results in LED ON, led2 results in LED OFF.
The problem is that i don't know how to set the port for the connection.
#include "mbed.h"
#include "USBSerial.h"
//Virtual serial port over USB
USBSerial serial;
int main(void) {
while(1)
{
serial.printf("I am a virtual serial port\r\n");
wait(1.0);
}
}
You can use the USBSerial interface to emulate a serial port over USB. You can use this serial port as an extra serial port or as a debug solution. It also communicates between Mbed and a computer.
I would like to do all the above(even thogh i don't know what does emulate a serial port over USB. What is that Virtual USB?).
I see that USBSerial constructor takes
USBSerial (bool connect_blocking=true, uint16_t vendor_id=0x1f00, uint16_t product_id=0x2012, uint16_t product_release=0x0001). And i think i need to modify some of this adresses. The problem is that on Windows the ports are represented in Device Manager with COMxx and on Linux like ttyACMxx. How would i transform this in hexa adresses - is this what i have to do?
You should not have to transform anything or mess with the USB product_id or vendor_id, an mbed serial port should show as any other serial port so if it doesn't for you it means you are having driver issues.
On most recent Linux distros the device should show something similar to the following kernel messages:
cdc_acm 5-2:1.1: ttyACM0: USB ACM device
usbcore: registered new interface driver cdc_acm
cdc_acm: v0.26:USB Abstract Control Model driver for USB modems and ISDN adapters
On Windows, you will probably need to install drivers. After you do that, the serial port should show as mbed Serial Port (COMx) on your Device Manager. There are many places you can get troubleshooting help, see here, for instance.
The fact that you are getting nothing on both Windows and Linux makes one wonder if you are using the right cable (some USB cables work only for charging and are no good for your purposes, and some others simply fail after a while). I would first make sure your cable works with other devices (obviously not for charging only). There is also the possibility your board went (or came from the factory) bad, but that's quite unlikely.
I just found this approach and it is working. The thing that i don't understand is why on my pc i get this message: b'Hello World!\n'
#include "mbed.h"
Serial pc(USBTX, USBRX); // tx, rx
int main() {
pc.baud(9600);
while(1)
{
pc.printf("Hello World!\n");
wait(0.1);
}
}
Ignore that 'b'. Your device is not seeing that 'b'. It is just being printed by serial terminal utility. Also I would like to mention what I got from your question is, you want to send some data from PC to board over Serial and if device receives that data, it should start blinking the LED. If that is correct, use the code below:
#include "mbed.h"
Serial pc(USBTX, USBRX); // tx, rx
DigitalOut led(LED1); // If blinking doesn't work with LED1, Check the pin map for your board and pass the LED pin instead of LED1
char token = 'a'; // This is the character that you should send to trigger blinking
bool startBlinking = false;
int main() {
pc.baud(9600);
while(1)
{
if (pc.getc() == token) {
startBlinking = true;
}
if (startBlinking) {
led = 1;
wait(0.2);
led = 0;
wait(0.8);
}
}
}
I am having a problem where I am unable to connect 2 SPI devices to my MkrZero. One device is a CAN shield and the other is an Ethernet Shield .
Both work fine on their own without the other connected but don't seem to work when both connected at the same time. This makes me think that my wiring and code is correct but something is still missing.
The CAN section initializes no matter what, execution seems to stop at the Ethernet code. But the Ethernet code works fine when the CAN wires are disconnected.
I have tried manually setting the CS pins, this doesn't do much. Even with both pins held high CAN starts up. This implies that the libraries take control of the pins.
I have read various tutorials on how to connect multiple devices with SPI but most times the devices are duplicates. My devices are different. I have connected the SPI pins exactly as I should with different CS pins for each device but the problem persists.
Here is my code
#include <CAN.h> //https://github.com/sandeepmistry/arduino-CAN
#include <MCP2515.h> //https://github.com/sandeepmistry/arduino-CAN Changes CS and Int pins from Uno to MkrZero configuration
#include <Ethernet.h>
#include <ArduinoJson.hpp>
#include <ArduinoJson.h>
#include <PubSubClient.h>
#include <Losant.h>
#include <SPI.h>
//Ethernet setup
#define MQTT_MAX_TRANSFER_SIZE 80 //this was done as a result of troubleshooting
byte mac[] = { 0x2C, 0xF7, 0xF1, 0x08, 0x19, 0x2C };
IPAddress ip (192, 168, 0, 1);
EthernetClient client;
// ** Configure the Losant credentials to enable comms over mqtt **
const char* LOSANT_DEVICE_ID = "";
const char* LOSANT_ACCESS_KEY = "";
const char* LOSANT_ACCESS_SECRET = "";
LosantDevice device(LOSANT_DEVICE_ID);
// The setup() function runs once each time the micro-controller starts
void setup()
{
Serial.begin(9600);
while(!Serial) { }
Serial.println("started UART");
//pinMode(BUTTON_PIN, INPUT);
// disable SD SPI
pinMode(4,OUTPUT);
digitalWrite(4,HIGH);
//starting ethernet
Ethernet.init(6); //changing Ethernet shield software select pin to 6 since default is an SPI transfer pin
Serial.println("ethernet about to start");
while(!Ethernet.begin(mac)) {}
Serial.println("ethernet started");
while (!CAN.begin(200E3)) {
Serial.println("Starting CAN failed! ");
delay(250);
}
Serial.println("CAN started");
delay(100);
Does anybody have any ideas on how to troubleshoot this or what the problem is?
There have been a few answers that involve not assuming the SPI bus has been kept the same from the last transaction. I need to reset SPI parameters each time I send a transaction. After going though the libraries I have seen that SPI.begintransaction() is called before every SPI communication, which according to my knowledge, resets the parameters. This implies that I don't need to manually change the SPI bus parameters before each transaction. Am I wrong in this regard?
SPI bus is really simple, and there should be no problem in connecting multiple slaves, as long as, at any time, only one of them has CS_ asserted (low). It can be expected that a library takes control of the CS_ pin, but it also should deassert it after the job is done. If not, then the library would be a very bad one.
You should check hardware and software.
Hardware: use a scope with memory, and look at what happens during initialization. May be that the CS_ pins need a pull-up resistor (the library, if it takes control of its CS_ pin, could choose to put it in high-impedance instead of driving it high). If you don't have a scope you can perhaps monitor the pins by software.
Software: may be the libraries use interrupts? In that case, the program can do things you are unaware of, for example a library can poll a device in background and mess with your code or one from another library. You have to dive deep in the libraries documentation and code. Again, a scope would help a lot.
You didn't specify much about your setup, and your tentative to "manually deselect the CS_ pins) is really worrying. You should check twice that point - if the CS_ pin is high, there is no reason the other SPI device does not work.
Problem was with the level shifter. I connected the OE enable pin to CAN CS, which puts the chip in high impedance when it isn't being used and enables it when I want to send data.
I used my scope to connect to the SPI pins and recorded what was happening. All was working well till I got to MISO. The level shifter was messing with it and pulling it to ground. Basically silencing output from one of the shields.
The learning here is when using level shifters with SPI devices connect the CS to OE.
I have a /dev/ttyUSB device and a /dev/ttyMFD device that I need to stream to logfiles. For the USB device I could use termios and configure it through that. This was pretty straight forward and there was a bit of documentation for this as well.
I can't seem to find any for the MFD though. Some places call it a MultiFuctionDevice and others call it the Medfield High Speed UART device.
Which is correct in the first place?
And secondly, can I open it in the same way that I open up a regular ttyUSB device?
Here is the snippit I use to open USB devices.
int fd = open(USBDEVICE0, O_RDWR);
struct termios io;
memset(&io, 0, sizeof(io));
io.c_iflag = 0;
io.c_oflag = 0;
io.c_cflag = CS8|CREAD|CLOCAL; // 8n1, see termios.h for more information
io.c_lflag = 0;
// TODO -- Since we are operating in non-blocking mode; confirm VMIN and VTIME settings have no effect on duration of the read() call.
io.c_cc[VMIN] = 1;
io.c_cc[VTIME] = 5;
speed_t speedSymbol = B921600;
cfsetospeed(&io, speedSymbol);
cfsetispeed(&io, speedSymbol);
int retVal;
retVal = tcsetattr(fd, TCSANOW, &io);
tcflush(fd, TCIOFLUSH);
usleep(100);
EDIT
For anyone who comes across this, there is one caveat. You must open the device in raw mode and dump everything into a log file. Parsing must be done post. Everything will come out as raw data but if you try to do any kind of configuration, the devices buffer will not be able to capture all the data, hold it, and process it in time before more data comes along.
MFD in Linux kernel is common abbreviation to Multi-Functional Device, Legacy serial driver for Edison abuses that and uses it's own interpretation as you mentioned: Medfield. In the upstream kernel the abbreviation MID is used regarding to Mobile Internet Device. In particular the serial driver is called drivers/tty/serial/8250_mid.c. See https://en.wikipedia.org/wiki/Mobile_Internet_device.
Yes, you may do the same operations as you did on top of /dev/ttyUSBx.
I'm building a growbox/terrarium with arduino uno as the temperature controller. Simple sketch for arduino: if DS18B20 sensor giv less than 25'C than turn on relay, which the heating cable is connected to. Loop 30s, every time Serial.print(temperature) to the PC, where I'm collecting data and make timelapse photos. ---> Here is the problem.
After some time (from 15 min up to 4 hours). Serial communication with PC stops. When I'm trying to upload a new sketch to arduino I got an error msg:
avrdude: ser_open(): can't set com-state for "\.\COM3"
I need to unplug and plug in again USB cable (or turn it off and on in Windows Device Manager), also restart Python app that is collecting data. (Very unsatisfactory fix).
So my questions are:
1. Why?
2. How to fix it?
3. Or how to make some workaround, for example reset COM port from code (desirably python2.7)
PS. Example of what I'm doing and how it works(and do not works) here: Life of pepper
PS2. My goal is to make controllable habitate for plants, where I can see behaviours differs depending on the temp, day-duration, light-intensity, humidity.
Please help me :-) .
ARDUINO UNO SKETCH
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the A
rduino
#define ONE_WIRE_BUS 2
#define PIN_HEATING 6
float temperatura = 30.0;
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
void setup(void)
{
// start serial port
Serial.begin(9600);
// Start up the library
sensors.begin();
pinMode(PIN_HEATING, OUTPUT);
}
void loop(void)
{
delay(30000);
if(temperatura<25.0){
digitalWrite(PIN_HEATING, HIGH);
}
else{
digitalWrite(PIN_HEATING, LOW);
}
sensors.requestTemperatures(); // Send the command to get temperatures
// After we got the temperatures, we can print them here.
// We use the function ByIndex, and as an example get the temperature from the first sensor only.
temperatura = sensors.getTempCByIndex(0);
Serial.print("#");
Serial.print(temperatura);
}
PYTHON2.7 RECEIVING PART
import serial #pySerial
ser = serial.Serial()
ser.port = "COM3"
ser.open()
data = ser.read_all().split("#")
datasize = len(data)
if datasize>1:
temp = data[datasize-1]
tempstr = " / " + str(temp) + "'C"
else:
tempstr=" / ----- "
I've experienced a similar problem in the past where the serial port would become unresponsive. The solution in my case wasn't very intuitive but worked nonetheless. Here's what might help you too:
Make sure the USB port you're using to connect the arduino isn't shared with other devices (for example using a USB multipoint hub). If you're using a desktop with multiple USB ports (usually found at the back of the desktop case), unplug all other USB devices (keyboard, mouse etc) and reconnect them to a different USB. Use the fastest USB port available available on your computer (USB 3.0 if present) and have it dedicated for communication with the arduino. The reason the communication port goes unresponsive sometimes is because of the momentary fluctuations in the voltage and/or current if you have several devices connected to it.
Make sure you have a stable power supply for you computer, voltage drifts due to heavy loading can sometimes cause PC peripherals to disconnect/reconnect.
If this doesn't work, try this quick software fix. Just before the delay(30000) statement in your code, add the following:
if(!Serial) { //check if Serial is available... if not,
Serial.end(); // close serial port
delay(100); //wait 100 millis
Serial.begin(9600); // reenable serial again
}
This might at least ensure continued operation operation of your application with out requiring your intervention.
So in my case the problem was solved by connecting my computer through proper surge supressor, formerly i had connectected it to wall with a simple cable, and I have old 2 wire, non-grounded, electric installation in my flat.
What's the problem in given code? Why it is not showing the output for rs232 when we connect it by the d-9 connector with the short of pin number 2 & 3 in that?
#include <bios.h>
#include <conio.h>
#define COM1 0
#define DATA_READY 0x100
#define SETTINGS ( 0x80 | 0x02 | 0x00 | 0x00)
int main(void)
{
int in, out, status;
bioscom(0, SETTINGS, COM1); /*initialize the port*/
cprintf("Data sent to you: ");
while (1)
{
status = bioscom(3, 0, COM1); /*wait until get a data*/
if (status & DATA_READY)
if ((out = bioscom(2, 0, COM1) & 0x7F) != 0) /*input a data*/
putch(out);
if (kbhit())
{
if ((in = getch()) == 27) /* ASCII of Esc*/
break;
bioscom(1, in, COM1); /*output a data*/
}
}
return 0;
}
Well, the code looks alright. Have you really connected the remaining pins correctly in the plug, see serial and pin connections.
Nothing obvious stands out from your code as the cause. Check all your bases as you are dealing with hardware/software. The following Microsoft article has a different implementation using _bios_serialcom (from bios.h) which might be a good reference point for you.
http://support.microsoft.com/kb/39501
Suggestions for where to go from here:
I would also suggest replacing the literals (eg 0x08) using the constants predefined for Baud rate, Parity (eg _COM_NOPARITY) to make the code more readable in your question.
Check that the Com port is actually open, as its a assumption which is unchecked in your code example above.
Also check up on the pin connections for the DB9. To connect two computers/devices you will need to null modem it, eg pin 2 to pin 3 at the other end, plus the Signal Ground. Make sure you are disabling/not looking for DTR.
If the other computer/device is setup then I would suggest first running HyperTerminal (Programs->Accessories->Communication) and connect to your COM 1 and check you can see characters from the other device. If not its most likely related to your cable.
Hope that helps.
Before checking with your code always check your serial communication with a terminal program. I don't have much experience with Windows environment but in Linux you have programs like cutecom or gtkterm where you can send/receive data from serial port. We extensively used these programs for serial communication in Linux, they are great for debugging potential problems with serial port interface (both h/w & s/w as well). So, before suspecting your code check with a terminal emulator program.
Hey, I am not an expert on Win32, but it seems to be easier to use another article as a source (the one mentioned here before looks outdated):
http://msdn.microsoft.com/en-us/library/ms810467
This is old too, dated around 1995, but it looks still effective. The NT architecture is very restrictive when it comes to grant access to hardware, for example, to send bytes to a PC paralell port one needs to rely on workarounds dll written by open source developers.
I'm assuming from your comment about "pin 2 & 3" that you've connected a loopback cable, so you're expecting to see anything you type come up on the screen (and stop doing so when you disconnect the cable).
I think there's a bug in the code: the if (kbhit()) is inside the if (status & DATA_READY).
That means you only check the keyboard if there is some input ready to receive from the serial port - which there won't be, because you haven't sent it anything yet! Try moving the test and see if it improves matters.
(Here is some similar serial port code that puts the if (kbhit()) test outside the DATA_READY check. It doesn't claim to work, but provides some evidence that this might be the source of the problem...)