I am a new programmer, so I am having a bit of problem with Serial communication of Arduino.
I am trying to read data from serial Input, sent by a simulation as characters and I need to store it as integer to write it with my servo.
I found this https://forum.arduino.cc/t/serial-input-basics-updated/382007 tutorial and example 4 does the job,
However, the simulation sends the data so fast that Arduino bottlenecks and the data pile up in the serial port and even if I stop the simulation the Arduino continues to perform the messages.
How can I slow down the data receiving like read data every 0.3 seconds instead. I tried to put some delays but it seems like it doesn't work.
Also, how can I change the code in a way that it stops performing new thing when there is no new serial messages and cancel the ones in the queue?
const byte numChars = 32;
char receivedChars[numChars]; // an array to store the received data
boolean newData = false;
//SERVO//
#include <Servo.h>
Servo myservo; // create servo object to control a servo
////////////////////////
int dataNumber = 0; // new for this version
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
myservo.attach(9); // attaches the servo on pin 9 to the servo object
Serial.println("<Arduino is ready>");
}
void loop() {
recvWithEndMarker();
showNewNumber();
}
void recvWithEndMarker() {
static byte ndx = 0;
char endMarker = '\n';
char rc;
if (Serial.available()> 0) {
rc = Serial.read();
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
ndx = 0;
newData = true;
delay(1);
}
}
}
void showNewNumber() {
if (newData == true) {
dataNumber = 0; // new for this version
dataNumber = atoi(receivedChars); // new for this version
Serial.print("This just in ... ");
Serial.println(receivedChars);
Serial.print("Data as Number ... "); // new for this version
Serial.println(dataNumber); // new for this version
myservo.write(dataNumber); // sets the servo position according to the scaled value
delay(50);
newData = false;
}
}
Thanks!
Welcome to the forum.
I'll admit that I don't know about your arduino set-up, but I hope that I can help.
Serial ports are asynchronous sources of data.
For base 3 wire RS-232, the receiver can't control the speed at which data is received other than baud rate hence received data is copied into a buffer (array) before it is processed.
This is to give your code time to process the received data before more messages arrive and cause what is known as a buffer overrun, corrupting data already received. Think of the serial link as being a hose pipe filling a bucket (the buffer) with water and you emptying it using a cup (your processing).
If your processing code is running too slowly and you are losing data then one option is to increase the size of the reception buffer, say from 32 to 255.
Adding delays to the reception code will only make matters worse.
An important point to make is that you must ensure that any processed data is removed from the buffer otherwise it will be processed again.
If your processing is fast enough then a nasty method is to just clear the buffer of all data by setting all array values to 0.
Another method is to use is keep a records (index value) of the next available location to write to and read from.
Data from the serial port is written into the buffer address using the write index value saved previously as a starting point. It is updated to take into account the size of the data written and incremented to indicate where to start the next write operation.
Your processing reads from the buffer using the last read index until it detects your end of message indicator and increments the read index to indicate the next location to read from.
It may be that your arduino serial port supports hardware flow control raising a Ready To Receive line when the hardware buffer (in the serial port itself) is full. This would be set before you open it.
Code:
Remove Delay calls - they only slow down your code.
Sending data out Serial.print, Serial.println commands take time,
place those after myservo.write
Remove Serial.print type commands that aren't strictly necessary.
Related
I have tried to create wireless PWM transmission using 433Mhz transmitter modules. I found this library for transmiting https://github.com/zeitgeist87/RFTransmitter and this library for reading PWM values on some pin https://github.com/xkam1x/Arduino-PWM-Reader.
I wrote code for sending PWM values:
#include "PWM.hpp"
#include <RFTransmitter.h>
#define NODE_ID 1
#define OUTPUT_PIN 11
RFTransmitter transmitter(OUTPUT_PIN, NODE_ID);
PWM my_pwm(2);
// the setup function runs once when you press reset or power the board
void setup() {
my_pwm.begin(true);
}
// the loop function runs over and over again forever
void loop() {
int pwmValue = my_pwm.getValue();
char stringValue[4];
itoa(pwmValue, stringValue, 10);
transmitter.send(stringValue, strlen(stringValue) + 1);
}
And similar code for receiving
#include <Servo.h>
#include <PinChangeInterruptHandler.h>
#include <RFReceiver.h>
int PWM_out_pin = 9;
Servo servo;
// Listen on digital pin 2
RFReceiver receiver(2);
void setup() {
servo.attach(PWM_out_pin);
receiver.begin();
}
void loop() {
char msg[MAX_PACKAGE_SIZE];
byte senderId = 0;
byte packageId = 0;
byte len = receiver.recvPackage((byte *)msg, &senderId, &packageId);
String *stringObject = new String(msg);
servo.writeMicroseconds(stringObject->toInt());
}
It works, but it has few problems.
First is that is not optimal. I transforming all to string. How can I send int values from PWM directly?
Second problem is that it has about 1 sec delay. Does it possible make it faster? I need it for realtime controlling servo.
Thanks.
How can I send int values from PWM directly?
transmitter.send((char*)pwmValue, sizeof(int));
Then on the receive side, you don't need to convert the data to a string.
servo.writeMicroseconds((int) *msg);
(int) and (char*) are C-style typecasts. Essentially telling the compiler that you want the data to be interpreted as a different variable type.
WARNING: Be careful with the "new" keyword, you are dynamically allocating memory. Rule of thumb in C/C++: Wherever you use the "new" keyword there should be a corresponding "delete" call that cleans up the memory.
In terms of making it faster, your C/C++ code here is not the limiting factor causing the 1 sec latency from command to action. Most likely there is a parameter in your RF transmitter transmit/receive stack that needs to be tweaked. Before sending PWM values, I would benchmark your latency of sending and printing out a simple string.
I am new to Arduino. I'm looking at Makecourse tutorial on RC522 RFID reader/writer
I pasted the script below. Basically, it detects a card then writes some data into block 2.
After compiling and uploading the code, I put up one card to the RFID reader, it works - but just once. When i put up a second card, nothing happens. Why?
I've compared this script with other example scripts from the mfrc522 library, they are pretty similar - in the void loop() section, it checks if NewCardPresent... ReadCardSerial... then proceeds to run the intended action. I've tried those example scripts and I can keep presenting a new card to the reader and the script will run again.
However this sketch, the loops only ran once. Is it the structure? How can i edit it to make it run continuously? I apologise if the answer is very simple :/
#include <SPI.h>//include the SPI bus library
#include <MFRC522.h>//include the RFID reader library
#define SS_PIN 10 //slave select pin
#define RST_PIN 5 //reset pin
MFRC522 mfrc522(SS_PIN, RST_PIN); // instatiate a MFRC522 reader object.
MFRC522::MIFARE_Key key;//create a MIFARE_Key struct named 'key', which will hold the card information
void setup() {
Serial.begin(9600); // Initialize serial communications with the PC
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card (in case you wonder what PCD means: proximity coupling device)
Serial.println("Scan a MIFARE Classic card");
// Prepare the security key for the read and write functions - all six key bytes are set to 0xFF at chip delivery from the factory.
// Since the cards in the kit are new and the keys were never defined, they are 0xFF
// if we had a card that was programmed by someone else, we would need to know the key to be able to access it. This key would then need to be stored in 'key' instead.
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;//keyByte is defined in the "MIFARE_Key" 'struct' definition in the .h file of the library
}
}
int block=2;//this is the block number we will write into and then read. Do not write into 'sector trailer' block, since this can make the block unusable.
byte blockcontent[16] = {"makecourse_____"};//an array with 16 bytes to be written into one of the 64 card blocks is defined
//byte blockcontent[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//all zeros. This can be used to delete a block.
byte readbackblock[18];//This array is used for reading out a block. The MIFARE_Read method requires a buffer that is at least 18 bytes to hold the 16 bytes of a block.
void loop()
{
/*****************************************establishing contact with a tag/card**********************************************************************/
// Look for new cards (in case you wonder what PICC means: proximity integrated circuit card)
if ( ! mfrc522.PICC_IsNewCardPresent()) {//if PICC_IsNewCardPresent returns 1, a new card has been found and we continue
return;//if it did not find a new card is returns a '0' and we return to the start of the loop
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {//if PICC_ReadCardSerial returns 1, the "uid" struct (see MFRC522.h lines 238-45)) contains the ID of the read card.
return;//if it returns a '0' something went wrong and we return to the start of the loop
}
// Among other things, the PICC_ReadCardSerial() method reads the UID and the SAK (Select acknowledge) into the mfrc522.uid struct, which is also instantiated
// during this process.
// The UID is needed during the authentication process
//The Uid struct:
//typedef struct {
//byte size; // Number of bytes in the UID. 4, 7 or 10.
//byte uidByte[10]; //the user ID in 10 bytes.
//byte sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection.
//} Uid;
Serial.println("card selected");
/*****************************************writing and reading a block on the card**********************************************************************/
writeBlock(block, blockcontent);//the blockcontent array is written into the card block
//mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
//The 'PICC_DumpToSerial' method 'dumps' the entire MIFARE data block into the serial monitor. Very useful while programming a sketch with the RFID reader...
//Notes:
//(1) MIFARE cards conceal key A in all trailer blocks, and shows 0x00 instead of 0xFF. This is a secutiry feature. Key B appears to be public by default.
//(2) The card needs to be on the reader for the entire duration of the dump. If it is removed prematurely, the dump interrupts and an error message will appear.
//(3) The dump takes longer than the time alloted for interaction per pairing between reader and card, i.e. the readBlock function below will produce a timeout if
// the dump is used.
//mfrc522.PICC_DumpToSerial(&(mfrc522.uid));//uncomment this if you want to see the entire 1k memory with the block written into it.
readBlock(block, readbackblock);//read the block back
Serial.print("read block: ");
for (int j=0 ; j<16 ; j++)//print the block contents
{
Serial.write (readbackblock[j]);//Serial.write() transmits the ASCII numbers as human readable characters to serial monitor
}
Serial.println("");
}
and this is the function that came along with the sketch:
int writeBlock(int blockNumber, byte arrayAddress[])
{
//this makes sure that we only write into data blocks. Every 4th block is a trailer block for the access/security info.
int largestModulo4Number=blockNumber/4*4;
int trailerBlock=largestModulo4Number+3;//determine trailer block for the sector
if (blockNumber > 2 && (blockNumber+1)%4 == 0){Serial.print(blockNumber);Serial.println(" is a trailer block:");return 2;}//block number is a trailer block (modulo 4); quit and send error code 2
Serial.print(blockNumber);
Serial.println(" is a data block:");
/*****************************************authentication of the desired block for access***********************************************************/
byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
//byte PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid);
//this method is used to authenticate a certain block for writing or reading
//command: See enumerations above -> PICC_CMD_MF_AUTH_KEY_A = 0x60 (=1100000), // this command performs authentication with Key A
//blockAddr is the number of the block from 0 to 15.
//MIFARE_Key *key is a pointer to the MIFARE_Key struct defined above, this struct needs to be defined for each block. New cards have all A/B= FF FF FF FF FF FF
//Uid *uid is a pointer to the UID struct that contains the user ID of the card.
if (status != MFRC522::STATUS_OK) {
Serial.print("PCD_Authenticate() failed: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return 3;//return "3" as error message
}
//it appears the authentication needs to be made before every block read/write within a specific sector.
//If a different sector is being authenticated access to the previous one is lost.
/*****************************************writing the block***********************************************************/
status = mfrc522.MIFARE_Write(blockNumber, arrayAddress, 16);//valueBlockA is the block number, MIFARE_Write(block number (0-15), byte array containing 16 values, number of bytes in block (=16))
//status = mfrc522.MIFARE_Write(9, value1Block, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print("MIFARE_Write() failed: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return 4;//return "4" as error message
}
Serial.println("block was written");
}
int readBlock(int blockNumber, byte arrayAddress[])
{
int largestModulo4Number=blockNumber/4*4;
int trailerBlock=largestModulo4Number+3;//determine trailer block for the sector
/*****************************************authentication of the desired block for access***********************************************************/
byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
//byte PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid);
//this method is used to authenticate a certain block for writing or reading
//command: See enumerations above -> PICC_CMD_MF_AUTH_KEY_A = 0x60 (=1100000), // this command performs authentication with Key A
//blockAddr is the number of the block from 0 to 15.
//MIFARE_Key *key is a pointer to the MIFARE_Key struct defined above, this struct needs to be defined for each block. New cards have all A/B= FF FF FF FF FF FF
//Uid *uid is a pointer to the UID struct that contains the user ID of the card.
if (status != MFRC522::STATUS_OK) {
Serial.print("PCD_Authenticate() failed (read): ");
Serial.println(mfrc522.GetStatusCodeName(status));
return 3;//return "3" as error message
}
//it appears the authentication needs to be made before every block read/write within a specific sector.
//If a different sector is being authenticated access to the previous one is lost.
/*****************************************reading a block***********************************************************/
byte buffersize = 18;//we need to define a variable with the read buffer size, since the MIFARE_Read method below needs a pointer to the variable that contains the size...
status = mfrc522.MIFARE_Read(blockNumber, arrayAddress, &buffersize);//&buffersize is a pointer to the buffersize variable; MIFARE_Read requires a pointer instead of just a number
if (status != MFRC522::STATUS_OK) {
Serial.print("MIFARE_read() failed: ");
Serial.println(mfrc522.GetStatusCodeName(status));
return 4;//return "4" as error message
}
Serial.println("block was read");
}
Arduino Loop() is supposed to be called infinitely unlike you observed.
Therefore you need to check two possibilities as below.
1) any return is happening at the beginning of the loop(). I saw two return statements. You'd better insert debug messages among them so that you could know how far reached before returning this function.
2) any blocking is happing in the loop(). I don't know but you'd better check this as well.
Turns out adding this few lines at the end of the script resolved the issue:
delay(1000);
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
:)
I think the problem is not with the loop. It is ruining continuously. You can check it by using some serial print in the beginning of the loop. Problem is after a card is detected the first time this program doesn't identify a card the second time.So focus on that. The loop is fine.
Specific problem
I am trying to communicate between a Python script and my Arduino Pro Mini using serial with a USB cable. In Python I can read what the Arduino writes but the Arduino can either not read what the Python writes or it is reading it in a form that I can't process. The most obvious way this manifests itself is when I try to write what the Arduino has just read from Python, to Python. Nothing gets displayed in Python.
My Arduino sketch appears to work just fine when I work with it in the Serial Monitor, i.e. it can read and write so I suspect it is something at the Python end. What's causing this?
Details
I want to be able test how quickly I can send 64 byte-long pieces of information to my Arduino. To do this I wrote a Python script that could should be able to periodically send various information to my Arduino. I can change the period and thus test what kind of speed I can get.
However, despite the fact that my Arduino sketch works just fine with Serial Monitor, i.e. I can send it information and read information from it, I can't get it to work with Python.
With my Python script I can open the connection and read what the Arduino writes, but for some reason there is a problem with writing from Python. I have tried the following but none of them have worked:
Using an Uno instead of a Pro Mini.
Putting a Serial.println() statement in my get_data() function. Nothing gets printed.
Sending a string with the format "T(number);" and using sscanf() to extract the number. Using an if statement to examine the number and turn an onboard LED on. Did not work.
Sending a string beginning with "T" and using an if statement to see if it is stored in raw_data[]. It isn't.
Writing the data from Python at different times (i.e. using a longer delay).
Writing from Python using device.write(b"mystring") and device.write("mystring".encode())
I'm at a loss for what to try next…
Python (2.7) code
import serial, time, random, string
device = serial.Serial('/dev/tty.usbserial-A105YOZX', baudrate = 38400)
initial_time = time.time()
current_time = time.time()
counter = 0
control = 0
while counter < 1000:
current_time = time.time()
if current_time - initial_time > 0.1:
initial_time = current_time
counter += 1
device.read()
if current_time - initial_time > 1 and control == 0:
device.write(b"Message being sent")
control = 1
Arduino code
#define RAW_LEN 64 //max length of incoming string (buffer size is only 64 bytes)
//variables for incoming data
char raw_data[RAW_LEN];
int raw_data_i = 0; //index
unsigned long my_time;
int control = 0;
int for_print;
int get_data(int i) {
//fills up raw_data[] with serial data. Clears raw_data if it gets too long
if (Serial.available()>0) {
raw_data[i] = Serial.read();
Serial.println(raw_data[i]);
//Serial.println(raw_data[i]);
if (i<RAW_LEN-1){
i++;
}
}
return i;
}
void clear_data() {
//clears raw_data and resets raw_data_i
raw_data_i = 0;
memset(raw_data,'\0',RAW_LEN-1);
}
//print functions
void raw_print(char data[]) {
Serial.println("Received data: ");
//if (raw_data[0] == 'T') {digitalWrite(LED_BUILTIN, HIGH);}
for (int i=0; i< RAW_LEN;i++) {
Serial.println(data[i]);
}
}
void setup() {
Serial.begin(38400);
Serial.println("Restart");
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
}
void loop() {
my_time = millis();
raw_data_i = get_data(raw_data_i);
//clear raw_data if it gets full
if (raw_data_i == RAW_LEN-1) {
clear_data();
}
if (my_time > 5000 && control == 0) {
Serial.println("Here");
raw_print(raw_data);
control = 1;
}
}
The problem turned out to be in the Python code. In while loop the first if statement is True after 0.1s, and then executes 9 more times before the second if statement is True.
The problem with this is that device.read() just hangs if it does not receive a character. Because I wasn't sending enough characters from the Arduino the Python code paused at the device.read() and never got to the second if statement.
An annoying mistake that took a while to figure out, hopefully this question and answer will save someone else a lot of debugging time.
I'm trying to establish a SerialPort connection which transfers 16 bit data packages at a rate of 10-20 kHz. Im programming this in C++/CLI. The sender just enters an infinte while-loop after recieving the letter "s" and constantly sends 2 bytes with the data.
A Problem with the sending side is very unlikely, since a more simple approach works perfectly but too slow (in this approach, the reciever sends always an "a" first, and then gets 1 package consisting of 2 bytes. It leads to a speed of around 500Hz).
Here is the important part of this working but slow approach:
public: SerialPort^ port;
in main:
Parity p = (Parity)Enum::Parse(Parity::typeid, "None");
StopBits s = (StopBits)Enum::Parse(StopBits::typeid, "1");
port = gcnew SerialPort("COM16",384000,p,8,s);
port->Open();
and then doing as often as wanted:
port->Write("a");
int i = port->ReadByte();
int j = port->ReadByte();
This is now the actual approach im working with:
static int values[1000000];
static int counter = 0;
void reader(void)
{
SerialPort^ port;
Parity p = (Parity)Enum::Parse(Parity::typeid, "None");
StopBits s = (StopBits)Enum::Parse(StopBits::typeid, "1");
port = gcnew SerialPort("COM16",384000,p,8,s);
port->Open();
unsigned int i = 0;
unsigned int j = 0;
port->Write("s"); //with this command, the sender starts to send constantly
while(true)
{
i = port->ReadByte();
j = port->ReadByte();
values[counter] = j + (i*256);
counter++;
}
}
in main:
Thread^ readThread = gcnew Thread(gcnew ThreadStart(reader));
readThread->Start();
The counter increases (much more) rapidly at a rate of 18472 packages/s, but the values are somehow wrong.
Here is an example:
The value should look like this, with the last 4 bits changing randomly (its a signal of an analogue-digital converter):
111111001100111
Here are some values of the threaded solution given in the code:
1110011001100111
1110011000100111
1110011000100111
1110011000100111
So it looks like the connection reads the data in the middle of the package (to be exact: 3 bits too late). What can i do? I want to avoid a solution where this error is fixed later in the code while reading the packages like this, because I don't know if the the shifting error gets worse when I edit the reading code later, which I will do most likely.
Thanks in advance,
Nikolas
PS: If this helps, here is the code of the sender-side (an AtMega168), written in C.
uint8_t activate = 0;
void uart_puti16(uint16_t val) //function that writes the data to serial port
{
while ( !( UCSR0A & (1<<UDRE0)) ) //wait until serial port is ready
nop(); // wait 1 cycle
UDR0 = val >> 8; //write first byte to sending register
while ( !( UCSR0A & (1<<UDRE0)) ) //wait until serial port is ready
nop(); // wait 1 cycle
UDR0 = val & 0xFF; //write second byte to sending register
}
in main:
while(1)
{
if(active == 1)
{
uart_puti16(read()); //read is the function that gives a 16bit data set
}
}
ISR(USART_RX_vect) //interrupt-handler for a recieved byte
{
if(UDR0 == 'a') //if only 1 single data package is requested
{
uart_puti16(read());
}
if(UDR0 == 's') //for activating constant sending
{
active = 1;
}
if(UDR0 == 'e') //for deactivating constant sending
{
active = 0;
}
}
At the given bit rate of 384,000 you should get 38,400 bytes of data (8 bits of real data plus 2 framing bits) per second, or 19,200 two-byte values per second.
How fast is counter increasing in both instances? I would expect any modern computer to keep up with that rate whether using events or directly polling.
You do not show your simpler approach which is stated to work. I suggest you post that.
Also, set a breakpoint at the line
values[counter] = j + (i*256);
There, inspect i and j. Share the values you see for those variables on the very first iteration through the loop.
This is a guess based entirely on reading the code at http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx#Y228. With this caveat out of the way, here's my guess:
Your event handler is being called when data is available to read -- but you are only consuming two bytes of the available data. Your event handler may only be called every 1024 bytes. Or something similar. You might need to consume all the available data in the event handler for your program to continue as expected.
Try to re-write your handler to include a loop that reads until there is no more data available to consume.
I'm using an Arduino Uno R3 and when I send an AT command to my GSM shield via Serial, I get an increasing number from Serial.available().
Here is the example I have been using to debug:
void loop()
{
Serial.println("AT+CADC?");
delay(3000);
}
void serialEvent()
{
char * sensorValue;
int serial = Serial.available();
Serial.print("-");
Serial.print(serial);
Serial.println("-");
if(serial >0)
{
sensorValue = (char*) malloc(sizeof(char) * (serial +1));
int i;
for(i = 0; i < serial; i++)
{
sensorValue[i] = Serial.read();
//Serial.print(sensorValue[i]);
}
sensorValue[serial+1] = '\0';
Serial.print(sensorValue);
}
delay(2000);
}
The result I get from the serial monitor is:
-30-
-63-
-63-
-63-
...
Why does the number of bytes available start off at 30 and then max out at 63? This happens even when I use Serial.read(), which should consume the data in the buffer.
sensorValue[serial+1] = '\0';
Serial.print(sensorValue); // <== here
You send whatever you receive right back to the modem. Which promptly echoes it back. So once you got it going with an AT command, you'll forever loop sending the same bytes back and forth. Remove the Serial.print() calls.
I'd suggest you use the software serial library—SoftwareSerial—and use that to access the GSM modem, unless the GSM modem requires UART or RS-232 level signals.
Another alternative, which might be appropriate if most of your work involves talking to modems, would be an Arduino Mega 2560. It has four hardware serial interfaces, though it doesn't have RS-232 output signals. For that, you should be able to use one of the FTDI breakout boards.