Serial.write causing void loop program to stop (Digital Pins stop responding) - arduino

I have an issue trying to send some serial data through tx and rx to another arduino through a HC05 bluetooth module.
The overall project is developing a hybrid go kart and using the arduino as a simple ECU unit with a PID speed control over the PWM output controlling a DC motor. I have been working the project in steps and have go as far as setting up a footpedal with the arduino and controlling the electronic speed controller (ESC) directly. I have added a simple PID function to this along with a simple hall sensor to detect the speed and does require tuning but works great so far. Now the problem comes when I try to send data across over serial ports.
I have had the bluetooth modules connected with to separate arduinos and have successfully managed to send over data from one arduino with a pot input to another with a 3.5 inch TFT screen. When I try to integrate the master side of the project to the PID controlled DC motor the system freezes. I have since then removed the PID control and gone back to direct control and it still fails, i have tried commenting out the interrupt sequence for the encoder and put a static value for RPM and still freezes. the sending sequence works if I don't attempt to use any digital outputs. I am really confused. The code I have gone down to to try and debug this can be found below. This is not the full code and has been chopped to pieces to try and eliminate this fault. however in this code below, if I comment out the sendData function the system works and the motor spins with relative speed to the pedal input. as soon as I try to send the data the system runs for a seconds then freezes. the data is still being sent and the static readings are showing just the digital output seizes to work.
#include <TimerOne.h>
int previous = 0;
int Tavg = 0; // the average
int Tout = 0;
int throttle = A0;
const int numReadings = 10;
int readings[numReadings]; // the readings from the analog input
int readIndex = 0; // the index of the current reading
int total = 0; // the running total
int ESCPin = 5;
unsigned int counter=0;
int RPM;
long Time = 0;
long ReadInt = 0;
void docount() // counts from the speed sensor
{
counter++; // increase +1 the counter value
}
void timerIsr()
{
Timer1.detachInterrupt(); //stop the timer
Serial.print("Motor Speed: ");
RPM = (counter*75 ); // RPM= counterx10*60/8 (x10 for second, 8 counts in encoder, 60 minutes === 75x counter)
Serial.print(RPM);
Serial.println(" Rotation per min"); Serial.print(Tout);Serial.print("= "); Serial.print(Tout*0.01961);Serial.println("V");
counter=0; // reset counter to zero
Timer1.attachInterrupt( timerIsr ); //enable the timer
}
void ReadEnc (){
Timer1.initialize(100000); // set timer for 0.1sec
attachInterrupt(0, docount, RISING); // increase counter when speed sensor pin goes High
Timer1.attachInterrupt( timerIsr ); // enable the timer
}
void sendData(){
if (Serial.available()>0) {
if (Serial.read() == 0){
//Serial.println(sendChars);
int RPMout = RPM;
Serial.write("<");
//delay(2);
//Serial.write(sendChars);
Serial.write("Data is,");
//delay(2);
Serial.write( itoa (RPMout, 4,10));
//delay(2);
Serial.write(", 30, 48.35");
//delay(2);
Serial.write(">");
//delay(10);
Serial.println("");
}
}
}
void setup()
{
Serial.begin(9600);
pinMode(2, INPUT_PULLUP); // internal pullup input pin 2
pinMode(ESCPin, OUTPUT);
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0; }
Time = millis();
ReadInt = -100;
}
void ReadSensor (){
// get the sensor value
total = total - readings[readIndex];
// read from the sensor:
readings[readIndex] = analogRead(throttle);
//Serial.println(readings[readIndex]);
// add the reading to the total:
total = total + readings[readIndex];
// advance to the next position in the array:
readIndex = readIndex + 1;
// if we're at the end of the array...
if (readIndex >= numReadings) {
// ...wrap around to the beginning:
readIndex = 0;
}
// calculate the average:
Tavg = total / numReadings;
}
void loop(){
ReadSensor();
ReadEnc();
RPM = 1800;
Tout = map(Tavg, 180, 860, 0, 200);
if (Tout>0){
analogWrite(ESCPin, Tout);
}
if (Time > ReadInt + 5000) {
sendData (); // when this is commented it works fine, tried moving it everywhere
ReadInt = Time;
}
Time = millis();
}
If anyone has any ideas please let me know, and I know I probably haven't explained my problem well so if their is any questions or more details needed please ask.

Second parameter of itoa should be a pointer to output bufffer. but you do not need itoa. use Serial.print(RPM);. For string the print and write functions are the same, but for number print has a version for int

Related

Send Arduino serial commands while plotting

I have a simple PD Arduino controller to spin a motor. I want to use it to demonstrate system responses graphically. I have it working so I can give a target position using the serial monitor, but I want to be able to see the serial plot output at the same time. There seems to be a similar dialogue box in the Serial Plotter, but commands sent from there don't seem to be recognized. Is there a way to plot incoming serial data while also sending commands as described above? I don't mind if I need additional libraries, but I can't see why it shouldn't work natively since I can already send commands while receiving info using the Serial Monitor. Maybe I'm misunderstanding that process.
Any help would be very appreciated. See full code below:
// Clockwise rotation direction.
#define CW 1
// Counter clockwise rotation direction.
#define CCW 0
// Frequency of output PWM signal.
#define PWM_FREQ 25000
// Update rate in microseconds.
#define CYCLE_TIME 1000
// Rate of sending position data to PC.
#define PLOT_RATE 200
#define PLOT_COUNTER CYCLE_TIME/PLOT_RATE
// IO pins. //
// The pin connected to ENBble A on the driver.
const int ENB = 14;
// Pins connected to IN3 and IN4 on the driver (for controlling the rotation direction).
const int IN4 = 15;
const int IN3 = 16;
// Signal A wire of the encoder.
const int ENCA = 17;
// Signal B wire of the encoder.
const int ENCB = 18;
// Value of ENCA.
int enca = 0;
// Value of ENCB.
int encb = 0;
// Value of IN3.
int in3 = 0;
// Value of IN4.
int in4 = 0;
// Motors position measure by encoder.
volatile long int motorPos = 0;
// Communication variables. //
// The byte sent over serial to Teensy.
int incomingByte = 0;
// Input buffer for receiving user input over serial.
char inputBuffer[8];
int bufferCnt = 0;
// Counter for sending position over serial for plotting purposes.
int pltCounter = 0;
// Controller variables./ /
// Last motor position.
long int lastPos = 0;
// Target motor position.
int targetPos = 0;
// Position at the start of the control loop.
int currentPos = 0;
// Position at the start of the previous control loop.
int prevPos = 0;
// Change in position (for approximating the derivative).
int dP = 0;
// Position error.
int pError = 0;
// P term of the controller.
int pTerm = 0;
// D term of the controller.
int dTerm = 0;
// Speed (= voltage = duty cycle). Controller output mapped to duty cycle range.
int spd = 0;
// Controller output.
int contOut = 0;
// Ratio for transforming counts to degrees (1920 count / 360 deg)
float ratio = static_cast<float>(360)/static_cast<float>(1920);
// Controller tunable parameters. //
// P gain.
const int kP = 10;
// D gain.
const int kD = 0;
// Error in encoder pulses correponding to the minimum duty cycle.
const int minErr = 0;
// Error in encoder pulses corresponding to the maximum duty cycle.
const int maxErr = 1024;
// minDutyCycle and maxDutyCycle depend on PWM frequency and can be determined in dc_motor_speed_control . For example for frequency of 25k,
// minDutyCycle = 120 (Motor starts to move),
// maxDutyCycle = 190 (Motor speed reaches its maximum given the supplied voltage).
const int minDutyCycle = 120;
const int maxDutyCycle = 190;
// Controller update rate variables. //
// Difference in time between desired cycle period and its execution time (without any delay()s).
int cycleDiff;
// Control loop start time.
long int startTime;
// Control loop end time.
long int endTime;
// Plotting
float motorPosDeg = 0;
//Plotter p;
void setup() {
Serial.begin(9600);
// Initialize the pins.
pinMode(IN3,OUTPUT);
pinMode(IN4,OUTPUT);
pinMode(ENB,OUTPUT);
pinMode(ENCA,INPUT);
pinMode(ENCB,INPUT);
analogWriteFrequency(ENB, PWM_FREQ);
// Set the initial rotation direction.
setDirection(CCW);
// Start with the motor at rest.
analogWrite(ENB,0);
// Encoder interrupt.
attachInterrupt(digitalPinToInterrupt(ENCA), encoderAISRising, RISING);
attachInterrupt(digitalPinToInterrupt(ENCB), encoderBISRising, RISING);
//p.Begin();
//p.AddTimeGraph("Position v Time", 1000, "Position", motorPosDeg);
}
// *** Encoder interrupt routines. See "Understanding Quadrature Encoded Signals" here: https://www.pjrc.com/teensy/td_libs_Encoder.html" *** //
void encoderAISRising(){
if(digitalRead(ENCB) == HIGH)
motorPos++;
else
motorPos--;
attachInterrupt(digitalPinToInterrupt(ENCA), encoderAISFalling, FALLING);
}
void encoderAISFalling(){
if(digitalRead(ENCB) == LOW)
motorPos++;
else
motorPos--;
attachInterrupt(digitalPinToInterrupt(ENCA), encoderAISRising, RISING);
}
void encoderBISRising(){
if(digitalRead(ENCA) == LOW)
motorPos++;
else
motorPos--;
attachInterrupt(digitalPinToInterrupt(ENCB), encoderBISFalling, FALLING);
}
void encoderBISFalling(){
if(digitalRead(ENCA) == HIGH)
motorPos++;
else
motorPos--;
attachInterrupt(digitalPinToInterrupt(ENCB), encoderBISRising, RISING);
}
// *** ***//
// Default rotation direction is CCW.
void setDirection(bool dir){
// CCW
if (dir == CCW){
digitalWrite(IN3,HIGH);
digitalWrite(IN4,LOW);
}else{
digitalWrite(IN3,LOW);
digitalWrite(IN4,HIGH);
}
}
void loop() {
if (Serial.available() > 0) {
// Read the incoming bytes, until a next line character (Enter) is encountered.
while (1){
incomingByte = Serial.read();
// We have read all the bytes.
if (incomingByte == '\n' || incomingByte == '\r'){
Serial.read();
break;
}else{
// Store the byte in the buffer and move on to the next.
inputBuffer[bufferCnt] = incomingByte;
bufferCnt++;
}
}
// Add a NULL character to the end of the array. Required for using atoi.
inputBuffer[bufferCnt] = '\0';
bufferCnt = 0;
// Convert string to integer.
targetPos = atoi(inputBuffer);
targetPos = targetPos / ratio;
}
// int i = 0;
// if (i % 2 == 0){
// targetPos = 360;
// } else {
// targetPos = 0;
// }
startTime = micros();
// Get the latest motor position.
currentPos = motorPos;
// Position error.
//pError = targetPos - motorPos;
pError = targetPos - currentPos;
// P term of the controller.
pTerm = kP * pError;
dP = currentPos - prevPos;
// D term of the controller. CYCLE_TIME/1000 normalizes the denominator, otherwise dTerm would always be zero (integer division).
dTerm = kD * (dP/(CYCLE_TIME/1000));
contOut = pTerm + dTerm;
// Set the target duty cycle (i.e. speed (i.e. voltage)).
// Error (in terms of encoder pulses) in the range minErr-maxErr is mapped to speed range corresponding to minDutyCycle-maxDutyCycle.
// 4 parameters to tune here.
spd = map(abs(contOut),minErr,maxErr,minDutyCycle,maxDutyCycle);
// Set the direction according to sign of position error (CCW is positive), and then speed.
// One optimization would be calling analogWrite(ENB,abs(spd)) at the start or end of the loop instead
// (at the expense of readibility).
if (pError > 0){
setDirection(CCW);
analogWrite(ENB,abs(spd));
}else if (pError < 0){
setDirection(CW);
analogWrite(ENB,abs(spd));
}
if (pltCounter == PLOT_COUNTER){
float mtrPos = static_cast<float>(motorPos);
motorPosDeg = mtrPos * ratio;
Serial.print(int(motorPosDeg));
Serial.println();
pltCounter = 0;
}
pltCounter++;
prevPos = currentPos;
cycleDiff = micros() - startTime;
// Adjust the update rate.
if (cycleDiff < CYCLE_TIME){
delayMicroseconds(CYCLE_TIME - cycleDiff);
}
//i++;
}
From what i understand of the plot function it utilizes the main arduino connexion to work. Based on how the arduino uart work you can only have 1 com port connexion per com port. This means you can either have the plot or command line open for each uart connexion. It is possible with different version of arduino to have multiple com ports. On the arduino uno there is only one com port "Serial". On the mega i think there are 3 uart ports. If you use a external FTDI UART board you can have the plot window open for serial0 and have the FTDI board connected on Serial1 to have the command line window open. You will have to change your code a little to send commands to serial1.
Here are a couple links to help you.
https://docs.arduino.cc/tutorials/communication/TwoPortReceive
https://docs.arduino.cc/built-in-examples/communication/MultiSerialMega
https://www.amazon.fr/AZDelivery-Adaptateur-FT232RL-s%C3%A9rie-book/dp/B01N9RZK6I?th=1

I'm having trouble implementing the Atmega328 timer into my arduino networking

I am trying to implement error correction over an r/f communication between two arduinos. I tried adding a timer to it, in order to create a packet resend, but whenever it gets past the first send, it starts printing garbage ad infinity instead of doing the timer interrupt.
I tried messing around with the inside loop conditions some as well as trying to figure out what was wrong with the timer, but I couldn't figure it out. The problem seems to happen right around the first serial print, which is strange, because that part of the code is mostly unchanged.
(packets is a structure of two ints)
#include <ELECHOUSE_CC1101.h>
#include "packets.h"
// These examples are from the Electronics Cookbook by Simon Monk
// Connections (for an Arduino Uno)
// Arduino CC1101
// GND GND
// 3.3V VCC
// 10 CSN/SS **** Must be level shifted to 3.3V
// 11 SI/MOSI **** Must be level shifted to 3.3V
// 12 SO/MISO
// 13 SCK **** Must be level shifted to 3.3V
// 2 GD0
const int n = 61;
unsigned short int sequence = 0;
byte buffer[n] = "";
void setup() {
Serial.begin(9600);
Serial.println("Set line ending to New Line in Serial Monitor.");
Serial.println("Enter Message");
ELECHOUSE_cc1101.Init(F_433); // set frequency - F_433, F_868, F_965 MHz
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 0xFFFF; // Max value for overflow for now
TCCR1B |= (1 << CS12); // 256 prescaler
interrupts(); // enable all interrupts
}
Packet pckt, recieve;
ISR(TIMER1_OVR_vect){ // timer compare interrupt service routine
//Resend packet
ELECHOUSE_cc1101.SendData(buffer, pckt.data + pckt.seqNum);
int len = ELECHOUSE_cc1101.ReceiveData(buffer);
buffer[len] = '\0';
recieve.seqNum = buffer[n];
Serial.println("Interrupt");
}
void loop() {
if (Serial.available()) {
pckt.data = Serial.readBytesUntil('\n', buffer, n);
pckt.seqNum = sequence;
buffer[pckt.data] = '\0';
buffer[n-1] = pckt.seqNum;
Serial.println((char *)buffer);
ELECHOUSE_cc1101.SendData(buffer, pckt.data + pckt.seqNum);
TCNT1 = 0; // clear timer
TIMSK1 |= (1 << TOIE0); // enable timer compare interrupt
int len = ELECHOUSE_cc1101.ReceiveData(buffer);
while (recieve.seqNum <= sequence) {
}
TIMSK1 &= ~(1 << TOIE0); // turn off the timer interrupt
}
}
Sending data takes too long for interrupts. You should keep calls to send and receive buffers of data within the loop() function call tree. For example, sending a 12 bytes message via UART at 9600 bauds can take up to about 12ms.
You can use the timer interrupt to decrement a timeout counter, as is usually done on micro controllers, or use the millis() function to handle timings, as is easily done on Arduino.
I suggest you use the millis() function to compute timeouts.
example:
/* ... */
// I could not figure out what you were trying to do with
// pckt.seqNum.... Putting it at the end of the buffer
// makes no sense, so I've left it out.
// Moreover, its size is 2, so placing it at buffer[n-1] overflows the buffer...
enum machineState {
waitingForSerial,
waitingForResponse,
};
unsigned int time_sent; // Always use unsigned for variables holding millis()
// can use unsigned char for timeouts of 255
// milliseconds or less. unsigned int is good for about
// 65.535 seconds or less.
machineState state = waitingForSerial;
void loop()
{
switch(state)
{
case waitingForSerial:
pckt.data = Serial.readBytesUntil('\n', buffer, sizeof(buffer));
if (pckt.data > 0)
{
++pckt.seqNum;
Serial.write(buffer, pckt.data);
ELECHOUSE_cc1101.SetReceive();
ELECHOUSE_cc1101.SendData(buffer, pckt.data);
time_sent = millis();
state = waitingForResponse;
}
break;
case waitingForResponse:
if (ELECHOUSE_cc1101.CheckReceiveFlag())
{
auto len = ELECHOUSE_cc1101.ReceiveData(buffer)) // can use C++17 with duinos!!!
Serial.print("cc1101: ");
Serial.write(buffer, len);
state = waitingForSerial; // wait for another command from PC
}
// 1 second timeout, note the cast and subtraction, this is to avoid any
// issues with rollover of the millis() timestamp.
else if ((unsigned int)millis() - time_sent > 1000)
{
// resend ... stays stuck this way.
Serial.println("Retrying :(");
ELECHOUSE_cc1101.SendData(buffer, pckt.data);
time_sent = millis();
}
break;
default:
state = waitingForSerial;
Serial.println("unhandled state");
break;
}
}

Can read, but cannot write serial ports on Ubuntu 16.04

I have a strange problem with trying to write data to a serial port.
I am running Ubuntu 16.04 on a NUC7i7DNBE, and am trying to make a serial connection to an Arduino UNO. The Serial API that I am using is found here: http://docs.ros.org/kinetic/api/serial/html/classserial_1_1Serial.html
I have written a simple program which opens the serial port "ttyACM0" to communicate with the arduino. I have tested this code on another computer running Ubuntu 16.04, and everything worked fine, the only permissions I had to set where adding the user to the dialout group.
On the NUC, however, I have added the user to the dialout group. This allowed the program to read from the Arduino, but it still does not write to the Arduino. The Arduino IDE will write to the Arduino just fine, but my program will not.
I am assuming that I am having trouble with serial write permissions in Ubuntu.
Steps I have taken:
I have added the user to the dialout group
I have added a rule in /etc/udev/rules.d/ which states:
SUBSYSTEMS=="tty", GROUP="dialout", MODE="0666"
Afterward, I sent the commands:
sudo chown root:root /etc/udev/rules.d/50-AVCusb.rules
sudo chmod 0644 /etc/udev/rules.d/50-AVCusb.rules
udevadm control --reload-rules
I followed some information found on stack exchange to get to this point:
https://unix.stackexchange.com/questions/111593/allow-non-root-user-to-read-write-dev-files
I have tried using an FTDI device to write to the Arduino port. The FTDI device uses the ttyUSB0 port rather than the ttyACM0 port. The result was the same; can read, but can't write.
I have also run my external hard-drive on the NUC to see if there was any kind of hardware issue. When I ran the program from my external hard drive, I had no problem reading from and writing to the Arduino.
I have not dealt much with Ubuntu permissions or ports in general, please help me find and upload any other information that you may need in order to help me solve this problem.
Code on NUC:
#include <ros/ros.h>
#include <serial/serial.h>
using namespace serial;
Serial ser;
static const uint8_t MOTOR_ID = 0;
void writeMotor(uint8_t byte)
{
size_t size = 4;
uint8_t buffer[size];
buffer[0] = 'G'; //PID
buffer[1] = 'O';
buffer[2] = MOTOR_ID; //address
buffer[3] = byte; //data byte
ser.write(buffer, size);
}
int main() {
ros::init(argc, argv, "servo_esc_driver");
std::string port = "/dev/ttyACM0";
Timeout timeout = Timeout(0, 0, 0, 0, 0);
bytesize_t bytesize = eightbits;
parity_t parity = parity_none;
stopbits_t stopbits = stopbits_one;
flowcontrol_t flowcontrol = flowcontrol_none;
try{
ser.setPort(port);
ser.setBaudrate(115200);
ser.setTimeout(timeout);
ser.setBytesize(bytesize);
ser.setParity(parity);
ser.setStopbits(stopbits);
ser.setFlowcontrol(flowcontrol);
ser.open();
}
catch (SerialException e) {
ROS_FATAL_NAMED("Failed to connect to the Arduino UNO, %s.", e.what());
ros::shutdown();
return 0;
}
uint8_t byte = 90;
writeMotor(byte);
}
Full Code on Arduino
#include <Servo.h>
const byte N = 2;
//Servo esc;
//Servo servo;
Servo servo[N];
//int escPos = 90;
//int servoPos = 90;
int pos[N];
static const byte ESC_PIN = 7;
static const byte SERVO_PIN = 8;
static const byte RPM_FEEDBACK_PIN = 0; //interrpt 0, pin 2
static const byte SERVO_FEEDBACK_PIN = A0;
//const float MUL = 0.7058823529; //180/255
unsigned long lastTime_servoFeedback = 0;
static const byte MOTOR_ID = 0; //ID for differentiating data received and sent over serial connections
static const byte SERVO_ID = 1;
//added for motor data timeout safety feature
static const unsigned long MOTOR_DATA_TIMEOUT = 200; //4 x 50 ms (50 ms time period expected)
static unsigned long lastTimeMotorData = 0;
static const byte NEUTRAL = 90;
unsigned long last_rpm_pulse_update_ms = 0; //used for detecting a stopped car, and rejecting old data when writing to the serial port
unsigned long last_rpm_pulse_time_us = 0;//keeps track of rpms by comparing to system timer
static const long REV_PERIOD_MAX_US = 100000; //in us
unsigned long rev_period = REV_PERIOD_MAX_US; //100 ms is considered too long to be in motion
boolean forward = true;
/*Scratch that, I want these parameters set in ROS:
static const float wheel_radius = 0.05 // meters
static const float revs_to_mps_MUL = //assuming 2.85 gear ratio for brushless motor differential: https://forums.traxxas.com/showthread.php?9080733-Diff-gear-ratios
*/
//boolean rpm_period_updated = false; //rpms must be updated every 100 ms, otherwise the car has stopped, and velocity data should show 0 m/s
void rpm_feedback()
{
//Serial.println("in rpm_feedback");
last_rpm_pulse_update_ms = millis(); //notice the 'ms' here we want to use millisecond for checking whether or not data is valid. millis() can count up to 50 days while micros() only counts up to 70 minutes, thus millis() is used here.
unsigned long time_now = micros(); //use time now for accurate time calculations
unsigned long rev_period_temp = time_now - last_rpm_pulse_time_us; //get spur-gear revolution period
if(rev_period_temp > 0) rev_period = rev_period_temp; //revs are within
else rev_period = REV_PERIOD_MAX_US;
last_rpm_pulse_time_us = time_now; //using 'time_now' ensures that the time taken to get to this point in code does not interfere with rev_period accuracy - - - micros(); //reset time
if(pos[MOTOR_ID] < 90) //determine the direction that the vehicle is traveling in
{
forward = false;
}else forward = true;
//rpm_period_updated = true; not needed, only last_rpm_pulse_time_ms is needed for checking
}
void setup() {
// put your setup code here, to run once:
pinMode(RPM_FEEDBACK_PIN, INPUT_PULLUP);
attachInterrupt(RPM_FEEDBACK_PIN, rpm_feedback,FALLING); //arduino reference recommends using digitalPinToInterrupt(RPM_FEEDBACK_PIN) but the command is not recognized here
analogReference(EXTERNAL); //Using external reference for servo position
for(int i = 0; i < N; i++) //initialize
{
pos[i] = 90;
servo[i].attach(ESC_PIN + i);
}
Serial.begin(115200);
}
void loop() {
// put your main code here, to run repeatedly:
if(Serial.available() >= 1)
{
if(Serial.read() == 'G')
{
unsigned long t = millis();
while((Serial.available() < 3) && ((millis() - t) < 10)); //wait for the rest of the package, or timeout
if(Serial.available() >= 3)
{
char buf[3];
Serial.readBytes(buf, 3);
if((buf[0] == 'O') && (buf[1] >= 0) && (buf[1] < 2))
{
pos[buf[1]] = byte(buf[2]);
if(buf[1] == MOTOR_ID) lastTimeMotorData = millis(); //time stamp of last motor data retrieval
//Serial.print("buf[2]: ");
//Serial.println(byte(buf[2]), DEC);
//Serial.print("pos: ");
//Serial.println(pos[buf[1]]);
}
}
}
}
if((millis() - lastTimeMotorData) > MOTOR_DATA_TIMEOUT) pos[MOTOR_ID] = NEUTRAL; //stop the motor if data is not being received
for(int i = 0; i < N; i++)
{
servo[i].write(pos[i]);
}
if((millis() - lastTime_servoFeedback) >= 50) // 20Hz 20) //50Hz matches current ROS driver settings
{
lastTime_servoFeedback = millis();
int servo_feedback = analogRead(SERVO_FEEDBACK_PIN);
Serial.write('G'); //PID
Serial.write('O');
Serial.write(SERVO_ID);
//Serial.print(servo_feedback);
Serial.write(lowByte(servo_feedback));
Serial.write(highByte(servo_feedback));
//Serial.println(servo_feedback);
float rev_frequency;
if((last_rpm_pulse_update_ms + 100) < millis()) rev_frequency = 0; //use millis() since it can count up to 50 days, and will not have a chance of a hiccup after 70 minutes of using micros()
//instead, correct period when slowing down, also stop when the maximum threshold is reached
//if((micros() - last_rpm_pulse_time_us) >= REV_PERIOD_MAX_US) rev_frequency = 0; //car is stopped in this case. I decided not to try correcting the period as mentioned above
else rev_frequency = (float) 1/rev_period*1000000;
byte *rev_freq_bytes_to_transmit = (byte *) &rev_frequency;
if(forward == false) rev_frequency = -rev_frequency; //a negative frequency is used for reverse
Serial.write('G'); //PID
Serial.write('O');
Serial.write(MOTOR_ID); //used for addressing
Serial.write(rev_freq_bytes_to_transmit, 4);
}
}
Some good information may be:
snuc#usuavc:~$ udevadm info -a -n /dev/ttyACM0
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/tty/ttyACM0':
KERNEL=="ttyACM0"
SUBSYSTEM=="tty"
DRIVER==""
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0':
KERNELS=="1-4:1.0"
SUBSYSTEMS=="usb"
DRIVERS=="cdc_acm"
ATTRS{authorized}=="1"
ATTRS{bAlternateSetting}==" 0"
ATTRS{bInterfaceClass}=="02"
ATTRS{bInterfaceNumber}=="00"
ATTRS{bInterfaceProtocol}=="01"
ATTRS{bInterfaceSubClass}=="02"
ATTRS{bNumEndpoints}=="01"
ATTRS{bmCapabilities}=="6"
ATTRS{supports_autosuspend}=="1"
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-4':
KERNELS=="1-4"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{authorized}=="1"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bConfigurationValue}=="1"
ATTRS{bDeviceClass}=="02"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPacketSize0}=="8"
ATTRS{bMaxPower}=="100mA"
ATTRS{bNumConfigurations}=="1"
ATTRS{bNumInterfaces}==" 2"
ATTRS{bcdDevice}=="0001"
ATTRS{bmAttributes}=="c0"
ATTRS{busnum}=="1"
ATTRS{configuration}==""
ATTRS{devnum}=="4"
ATTRS{devpath}=="4"
ATTRS{idProduct}=="0043"
ATTRS{idVendor}=="2341"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="Arduino (www.arduino.cc)"
ATTRS{maxchild}=="0"
ATTRS{quirks}=="0x0"
ATTRS{removable}=="removable"
ATTRS{serial}=="55330313635351207081"
ATTRS{speed}=="12"
ATTRS{urbnum}=="6990"
ATTRS{version}==" 1.10"
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1':
KERNELS=="usb1"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{authorized}=="1"
ATTRS{authorized_default}=="1"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bConfigurationValue}=="1"
ATTRS{bDeviceClass}=="09"
ATTRS{bDeviceProtocol}=="01"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{bMaxPower}=="0mA"
ATTRS{bNumConfigurations}=="1"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bcdDevice}=="0415"
ATTRS{bmAttributes}=="e0"
ATTRS{busnum}=="1"
ATTRS{configuration}==""
ATTRS{devnum}=="1"
ATTRS{devpath}=="0"
ATTRS{idProduct}=="0002"
ATTRS{idVendor}=="1d6b"
ATTRS{interface_authorized_default}=="1"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="Linux 4.15.0-32-generic xhci-hcd"
ATTRS{maxchild}=="12"
ATTRS{product}=="xHCI Host Controller"
ATTRS{quirks}=="0x0"
ATTRS{removable}=="unknown"
ATTRS{serial}=="0000:00:14.0"
ATTRS{speed}=="480"
ATTRS{urbnum}=="76"
ATTRS{version}==" 2.00"
looking at parent device '/devices/pci0000:00/0000:00:14.0':
KERNELS=="0000:00:14.0"
SUBSYSTEMS=="pci"
DRIVERS=="xhci_hcd"
ATTRS{broken_parity_status}=="0"
ATTRS{class}=="0x0c0330"
ATTRS{consistent_dma_mask_bits}=="64"
ATTRS{d3cold_allowed}=="1"
ATTRS{dbc}=="disabled"
ATTRS{device}=="0x9d2f"
ATTRS{dma_mask_bits}=="64"
ATTRS{driver_override}=="(null)"
ATTRS{enable}=="1"
ATTRS{irq}=="122"
ATTRS{local_cpulist}=="0-7"
ATTRS{local_cpus}=="ff"
ATTRS{msi_bus}=="1"
ATTRS{numa_node}=="-1"
ATTRS{revision}=="0x21"
ATTRS{subsystem_device}=="0x2070"
ATTRS{subsystem_vendor}=="0x8086"
ATTRS{vendor}=="0x8086"
looking at parent device '/devices/pci0000:00':
KERNELS=="pci0000:00"
SUBSYSTEMS==""
DRIVERS==""
I decided that the problem was with the ROS version of serial. I decided to try some native linux library, termios, and had success writing to the port!
I found this example code:
https://en.wikibooks.org/wiki/Serial_Programming/Serial_Linux
The problem lies in the ros serial installation somehow.
Don't know if you still want to solve this with serial/serial.h, but I think that your problem might be in the timeout settings.
I'm telling you this, 'cause I had the exact same problem, I could read the incoming data, but couldn't write.
The /dev/ttyUSB0 permission was ok, but not the timeout.
I found the following config on internet, gave a try and work. Now I can read and write.
try{
ser.setPort("/dev/ttyUSB0");
ser.setBaudrate(9600);
serial::Timeout to = serial::Timeout::simpleTimeout(10);
ser.setTimeout(to);
ser.open();
return true;
}
catch (SerialException e) {
return 0;

Doppler Radar analogRead()

I've been doing a project on reading doppler speed and creating velocity data. I don't have a firm background on electric engineering / signal processing but I've researched quite a bit so far. Please bear with me if I get things wrong in my explanation.
I am currently using HB100 and CDM324 breakout model. The objective in this project is to get the voltage reading from the module and create a spectrogram though FFT in MATLAB. I got a sample data from BlackCatScience with the hand moving towards the Sensor at a fast speed. Its plot is shown below:
Hand Speed Using Doppler Radar
And I've assembled my arduino kit like the picture below:
Arduino-HB100_kit
Connections are:
VCC -> 5V GND -> GND FOUT -> Pin 8 VOUT -> Pin A5
So far, I found out that HB100 supports pulse/CW usage and used the code below to measure the frequency using HB100.
#include <MsTimer2.h>
#include <FreqMeasure.h>
//--------------GlOBAL VARIALBES---------------------
double raw_data = FreqMeasure.read();
double sum = 0;
int count = 0;
double raw_data_array[10];
unsigned long timeStamp = 0;
//---------------------------------------------------
void setup() {
Serial.begin(115200);
FreqMeasure.begin();
Serial.print("Time (ms)");
Serial.print("\t");
Serial.println("Hz");
}
void loop() {
timer();
freq_measure();
}
void timer() {
timeStamp+=1;
}
void freq_measure() {
while (timeStamp < 101) {
if(FreqMeasure.available()) {
//average readings
sum += FreqMeasure.read();
count ++;
if (count > 2) {
float frequency = FreqMeasure.countToFrequency(sum/count);
// Serial.print(timeStamp);
// Serial.print("\t");
Serial.println(frequency);
timeStamp++;
sum = 0;
count = 0;
}
}
}
}
The problem with this code is that, I want to measure the voltage reading at every, say for example, 1 millisec. However, this code is similar to using pulseIn function and the rate at which the data is out differs on whether I'm moving my hand to/from the sensor or not. When I'm not moving, the data output become slow, and when I'm moving, the data output rate is fast.
Hence, I decided to use some other code and use analogRead function like below:
//-----------------------------------------------------------------
#define RADAR A5 //pin number for VOUT
//--------------------GLOBAL VARIABLES-----------------------------
unsigned long timeStamp;
//-----------------------------------------------------------------
void setup() {
Serial.begin(115200);
pinMode(RADAR, INPUT);
Serial.println(F("Settings Complete" ));
}
void loop() {
// 1 millisec == 0.001 second --> 1000 millisec == 1 second
// we want about 5 seconds of data, hence the loop runs for 5000 millisec
while (timeStamp < 5000){
showReading();
}
}
void showReading() {
// timeStamp = millis();
// Serial.print(timeStamp);
// Serial.print("\t");
//Read input on analog pin 5:
int sensorData = analogRead(RADAR);
// float voltage = sensorData * (5.0 / 1023.0);
Serial.println(sensorData);
}
This time, the serial monitor is giving me a value between 0 and 1023 and that's great, but even if I move my hand in front of the sensor, the values change but minimally. What should I do to correct this and get a plot like the graph above?
Thanks for reading such a long question. Have a great day!

Arduino SD card fails to write when used with another SPI device

I have an ADXL355 accelerometer attached to an Adafruit Feather Adalogger. I can configure and read the sensor. I can also write binary values to the SD card. The problem occurs when I try to read from the sensor and then write that data to the SD card. The only thing I can think of is I'm somehow messing up the SPI communication but I can't see where. I looked through pins_arduino.h for my board and the SD Card (pin 4) is on a different register than pin 10 so I don't see how I'm breaking things.
My operations proceed like this. Global sensor creation, Serial.begin, SD.begin, SPI.begin, Test sensor connection, Create file for output on SD card, Initialize sensor, Read sensor FIFO, Write to file, repeat last 2 forever.
The file is created but remains at 0 file size, ie nothing is actually written to the card.
The sensor can operate at 4 kHz which was hard to achieve using the digitalWrite functions so I switched to using the port registers on the Feather. I do it like this:
#include <SM_ADXL355_SPI_fast.h>
#include <SPI.h>
#include <SD.h>
#define cardSelect 4
ADXL355_SPIF adxl355(&DDRB, &PORTB, _BV(6)); // values taken from pins_arduino.h, tested and working on pin 10
void setup() {
Serial.begin(57600);
while(!Serial){
// wait for Serial
}
SD.begin(cardSelect);
SPI.begin();
while(!adxl355.TestConnection()){
delay(1000);
}
adxl355.OpenFile("TestSPI.bin");
adxl355.Initialize(1, 10, 0); // set range to 2g's, frequency to 4 Hz and filter to off
}
void loop() {
while(true){ // avoid Arduino overhead of their loop function
adxl355.ReadFIFO();
adxl355.WriteFIFOToFile();
}
}
Here is the ADXL constructor
ADXL355_SPIF::ADXL355_SPIF(volatile uint8_t * outReg, volatile uint8_t * outPort, uint8_t bitValue) : sensorOutReg(outReg), sensorPort(outPort), sensorBitValue(bitValue){
*sensorOutReg |= sensorBitValue;
*sensorPort |= sensorBitValue;
sensorWriteCount = 0;
}
TestConnection tests that the DeviceID reads 0xAD. Initialize sets the G range, sample rate in Hz and filter. I have tested these with serial output and they work properly.
OpenFile looks like this:
bool ADXL355_SPIF::OpenFile(const String& fileName){
sensorFile = SD.open(fileName, FILE_WRITE);
if (!sensorFile){
Serial.print("Could not create file: ");
Serial.println(fileName);
return false;
}
return true;
}
After running this a file does get created on the SD card called "TESTSPI.BIN" with 0 file size.
ReadFIFO reads the numbers of entries in FIFO, stored as fifoCount and then populates a buffer (sensorFIFO[32][3]) with the values from the FIFO. I've printed this buffer to Serial to show that it's working. Here is that function
void ADXL355_SPIF::ReadFIFO(){
ReadRegister(ADXL355_RA_FIFO_ENTRIES, 1);
fifoCount = buffer[0];
ReadFIFOInternal();
return;
}
void ADXL355_SPIF::ReadFIFOInternal(){
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
*sensorPort &= ~sensorBitValue;
uint8_t spiCommand = ADXL355_RA_FIFO_DATA << 1 | ADXL355_READ;
SPI.transfer(spiCommand);
int i = 0;
unsigned long tempV;
unsigned long value;
while(i < fifoCount){
for (int ptr = 0; ptr < 3; ++ptr){
buffer[0] = SPI.transfer(0x0);
value = buffer[0];
value <<= 12;
tempV = SPI.transfer(0x0);
tempV <<= 4;
value |= tempV;
tempV = SPI.transfer(0x0);
tempV >>=4;
value |= tempV;
if (buffer[0] & 0x80) {
value |= 0xFFF00000;
}
long lValue = static_cast<long>(value);
sensorFIFO[i][ptr] = scaleFactor * lValue;
}
i += 3;
}
SPI.endTransaction();
*sensorPort |= sensorBitValue;
return;
}
Here is WriteFIFOToFile:
void ADXL355_SPIF::WriteFIFOToFile(){
if (fifoCount > 0){
sensorFile.write(reinterpret_cast<const char *>(&sensorFIFO), 4 * fifoCount);
}
sensorWriteCount += fifoCount;
if (sensorWriteCount >= 100){
sensorFile.flush();
sensorWriteCount = 0;
}
}
After allowing this to run for a while the file size is always 0. I tried a simple binary write function just to test the card. It looks like this and it worked.
#include <SD.h>
#define cardSelectPin 4
const float pi=3.14159;
File oFile;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while(!Serial){
// wait for serial
}
SD.begin(cardSelectPin);
oFile = SD.open("Test.bin", FILE_WRITE);
Serial.println(sizeof(int));
Serial.println(sizeof(float));
float testFloat[32][3];
for (int i = 0; i < 32; ++i){
for (int j = 0; j < 3; ++j){
testFloat[i][j] = pi * (i + 1) + j;
}
}
oFile.write(reinterpret_cast<const char *>(&testFloat), sizeof(float) * 96);
oFile.close();
Serial.println("Finished writing file.");
}
void loop() {
// put your main code here, to run repeatedly:
}
The problem was that flush was not being called correctly. I had created a buffer to hold data from the FIFO and it would flush the card when it would get full enough such that a subsequent read would overflow. At that time it would call flush. This is what was intended with the variable sensorWriteCount. This variable was of type uint8_t when it should have been a uint16_t.
Changing to the correct type fixed the problem. I would have deleted this question because it boils down to a typo, but once an answer has been posted the system doesn't allow that.
The only difference between the not-working sketch and the working one is the closing of the sd card. The sd card MUST be closed, I had the same problem you have and I assume that the file gets its boundaries written in its filesystem at file close call.
To solve your issue, use a push button. When you push it, it will close the file and stop reading/processing sensors. You can also use this button to start reading and recording sensors data on sd card again (toggle).

Resources