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!
Related
So I need some help reviewing and editing the code below. Is there a way to make it more efficient instead of what I did? Also, are there better ways to do some of the methods I did? Answers are appreciated greatly (I'm fairly new to Arduino).
The code below is for a corridor ledstrip that lights up when the distance sensor on the Arduino detects anything below a certain distancenear it (yes, I thought of using a motion sensor but I only had distance sensors on hand).
const int trigPin = 9;
const int echoPin = 10;
float duration, distance;
#include <FastLED.h>
#define LED_PIN 6
#define NUM_LEDS 60
int timingnum = 0;
//#define BRIGHTNESS bright
int bright = 100;///the brightness of the leds
int lednum = 60;///the number of leds available
int timer;
CRGB leds[NUM_LEDS];
void setup() {
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
Serial.begin(9600);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
// FastLED.setBrightness( BRIGHTNESS );
}
void loop() {
lights();
}
void sensor() { ///THIS IS CLEAN
digitalWrite(trigPin, LOW);
delayMicroseconds(timingnum);
digitalWrite(trigPin, HIGH);
delayMicroseconds(timingnum);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration*.0343)/2;
Serial.print("Distance: ");
Serial.println(distance);
}
// in the lights the reason why it wouldnt output the data for the
// represented while loops is because the while loop continued running
// without the data from the distance and thus it needs to constantly
// be updated in the while loop conditional statments
void lights() {
sensor(); //data update
while (distance <= 10) {
for (int x = 0; x <= 60; x++) {
leds[x] = CRGB(255,255,255);
FastLED.show();
sensor(); //sensor update
}
for (timer = 0; timer <=800; timer++) {
sensor();
}
sensor(); //replaces the data updates above
}
sensor(); //sensor update
while (distance >= 11) {
for (int x = 0; x <= 60; x++) {
leds[x] = CRGB(0,0,0);
FastLED.show();
}
sensor(); //data update
}
}
I would be tempted to make a little function to set the RGB colors of your LED strip: void fillup(byte r, byte g, byte b). Then you would call it in two places: fillup(0,0,0) and fillup(127,127,127) (yeah, not 100% on).
Also, I'm confused why you have so many sensor() calls in the first while loop. Seems like you only need to call it once when you need updated values for distance.
Also, the comments on the sensor calls are confusing... after reading them I have no idea what sensor is supposed to do. I tend to put a comment before functions that describes what they do, and put a stand-alone comment in the code to explain what the next "paragraph" does. And I avoid banners - they get in the way.
Do you want FastLED.show() inside the loop that updates the colors? Or just after the loop, to update the hardware after the array is changed? IDK, just asking.
I usually do not mind globals, but in this case you would be better off letting sensor return the distance. Then you could while ( sensor() <= 10 ). Or use a do.. while loop with one call to sensor at the bottom if you want to keep the global.
I'd try to get rid of the floating point, too... just calculate what your thresholds are in the raw echo values, and use those. Do we really care what the internal units of measurement are?
Sorry for the long unload... There's nothing wrong with what you have.
I built an app to observe real-time graph. I set the sampling rate to 256hz in my Arduino code. I gave the output of 100hz through a function generator, but I obtained a random waveform instead of the sine waveform.
this is my Arduino code:
#include <SoftwareSerial.h> //import Software Serial library
SoftwareSerial myStream(0, 1); //pins for Rx and Tx respectively
int ECG;
void setup()
{
// put your setup code here, to run once:
pinMode(A0, INPUT); //LDR
myStream.begin(115200);
Serial.begin(9600);
}
void loop()
{
ECG = analogRead(A0);
Serial.println(ECG);
if (myStream.available() > 0)
{
char re = myStream.read();
switch (re)
{
case 'E':
start();
break;
}
}
//about 256Hz sample rate
delayMicroseconds(3900);
}
void start()
{
while (1)
{
myStream.print('s');
myStream.print(floatMap(analogRead(ECG), 0, 1023, 0, 255), 2);
//about 256Hz sample rate
delayMicroseconds(3900);
if (Serial.available() > 0)
{
if (Serial.read() == 'Q')
return;
}
}
}
float floatMap(float x, float inMin, float inMax, float outMin, float outMax)
{
return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
}
This is my circuit diagram. In addition, I connected the oscilloscope output to A0 and common ground.
enter image description here
How can I get the sine waveform in my app.Any help on this would be greatly appreciated.
With your ~ 256 Hz sampling rate you just get about 2 asynchronous readings per period of a 100 Hz source.
analogRead adds (100 .. 150 µs on a 16 MHz Arduino) a considerable jitter to your delay.
Not sure about SoftwareSerial sending #115200. (receive won't work well at that speed)
And float is slow, too.
Not sure what you expect.
Check the raw data first:
void setup() {
Serial.begin(115200); // allows for 10 char/ms
}
void loop() {
static unsigned long oldmillis;
if (millis() != oldmillis) {
oldmillis = millis();
// 1 kHz sample rate :
int ECG = analogRead(A0)*100L / 1024;
Serial.println(ECG); // 0 .. 99
}
}
Perhaps add your start/stop communication or check with Arduino SerialPlotter.
When that's ok, test if you get those numbers via bluetooth to your app...
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
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).
This is the revised code, the initial value is reading ridiculously high without the conversion!! How do I get the conversion to apply to the initialTemp as well temperatureC?
The code you have helped with as far as I can tell is exactly what I was trying to achieve, but obviously.
Thanks again!!
int pin_tempRead = 0; // temperature sensor pin
int coolLED = 2; // cooling LED digital pin
int heatLED = 3; // heating LED digital pin
float initialTemp;
float cutOffTemp = 30; //cut off temperature = 30°C
void setup()
{
Serial.begin(9600); //Start the serial connection with the computer
pinMode(heatLED, OUTPUT); //initialise as OUTPUT
pinMode(coolLED, OUTPUT); //initialise as OUTPUT
initialTemp = analogRead(pin_tempRead); // read the initial temp
Serial.print("Initial temperature: "); Serial.print(initialTemp); Serial.println("C"); //prints out starting temperature
}
void loop() // run over and over again
{
//getting the voltage reading from the temperature sensor
float current_temp = analogRead(pin_tempRead);
// converting that reading to voltage
float voltage = current_temp * 5.0; voltage /= 1024.0;
float temperatureC = (voltage - 0.5) * 100 ; //converting from 10 mv per degree with 500 mV offset
//to degrees ((voltage - 500mV) times 100)
if(temperatureC > cutOffTemp) {
// temp too high -> turn on the cooling system
digitalWrite(heatLED, LOW);
digitalWrite(coolLED, HIGH);
}else if (temperatureC < initialTemp) {
// temp too low -> turn on the heating system
digitalWrite(heatLED, HIGH);
digitalWrite(coolLED, LOW);
}
Serial.print("Current pump temperature: ");
Serial.print(temperatureC); Serial.println("C");
delay(1000);
}
the loop() function executes over and over, so it's not a good place to store initial values.
What you need to do is define a global variable, initialize it inside the setup() function and then you can read it in the loop() function
Minimalist example:
int pin_tempRead = 0; // temperature sensor pin
float initial_temp; // define a global variable
void setup() {
initial_temp = analogRead(pin_tempRead); // read the initial temp
}
void loop() {
float current_temp = analogRead(pin_tempRead);
// get the temperature difference respect to the initial one
float difference = initial_temp - current_temp;
delay(1000);
}
PD: Also is a good practice to distinguish the variables defining a hardware connection (pins) from the software ones. I usually append pin_ to the variables that define connections. Otherwise is not clear if tempRead is the value of the temperature or the pin where the sensor is attached.
Also, for the turning on and off of the heater/cooling system: You are already in a loop (the loop() function is a loop) so you don't need a while loop.
And you have some problem with our logic.
As I understood, you want to heat until the higher threshold (cutOff) is reached, then cool down until the lower threshold is reached (initialTemperature).
This is called Hysteresis, but your logic was wrong, here's the corrected one:
Just do:
void loop() {
if(temperatureC > cutOffTemp) {
// temp too high -> turn on the cooling system
digitalWrite(heatLED, LOW);
digitalWrite(coolLED, HIGH);
}else if (temperatureC < initialTemp) {
// temp too low -> turn on the heating system
digitalWrite(heatLED, HIGH);
digitalWrite(coolLED, LOW);
}
Serial.print("Current pump temperature: ");
Serial.print(temperatureC); Serial.println("C");
delay(1000);
}
By the way, you are using the initialTemperature as the low threshold for turning on the heating.
Is that what you really want?
What if the initial temperature is higher that the cutOffTemp? You will have problems in that case since the lower threshold is higher than the higher threshold.