For no apparent compelling reason that I can see.
Also, you're attempting to read two bytes even though you don't know two bytes are available to be read.
For no apparent compelling reason that I can see.
Also, you're attempting to read two bytes even though you don't know two bytes are available to be read.
LED Pulses
The GP2Y1010AU0F data sheet calls for the sensor LED (pin 3) to be pulsed on for 0.32msec
every 10msec (100Hz).
between the controller and pin 3, so in reality pin 3 needs to be pulled LOW by the Arduino
to turn the LED on.
We can use the Arduino Timer 1 OC1B output (D10) to create the correct pulse-width
modulated waveform for the LED: low for 0.32msec every 10msec.
LED Pulses
The GP2Y1010AU0F data sheet calls for the sensor LED (pin 3) to be pulsed on for 0.32msec
every 10msec (100Hz).
Ahhh ... got it.
Also, you're attempting to read two bytes even though you don't know two bytes are available to be read
actually he waits for the first byte to arrive and then mySUART.readBytes() will wait for the bytes to arrive and has a builtin timeout.
[code]
#include<SoftwareSerial.h>
#include <TimerOne.h>
SoftwareSerial mySUART(7, 8); // D7, D8 = SRX, STX
const int numReadings = 100; // samples are taken at 100Hz so calculate average over 1sec
volatile int readings[numReadings]; // the readings from the analog input
volatile int readIndex = 0; // the index of the current reading
volatile long int total = 0; // the running total
volatile int latest_reading = 0; // the latest reading
volatile int average_reading = 0; // the average reading
int dustPin = A0;
float percent;
volatile bool dataReady = false;
void setup() {
//Serial.begin(9600);
mySUART.begin(9600);
// Configure PWM Dust Sensor Sample pin (OC1A)
pinMode(9, OUTPUT);
// Configure PWM Dust Sensor LED pin (OC1B)
pinMode(10, OUTPUT);
// Configure INT0 to receive Dust Sensor Samples
pinMode(2, INPUT_PULLUP);
// Put Timer 1 into 16-bit mode to generate the 0.32ms low LED pulses every 10ms
// A0 needs to be sampled 0.28ms after the falling edge of the LED pulse - via INT0 driven by OC1A (D9)
Timer1.initialize(10000); // Set a timer of length 10000 microseconds (or 10ms - or 100Hz)
Timer1.pwm(10, 991); // Set active high PWM of (10 - 0.32) * 1024 = 991
Timer1.pwm(9, 999); // Set active high PWM of (10 - 0.28) * 1024 = 995 BUT requires a fiddle factor making it 999
// Attach the INT0 interrupt service routine
attachInterrupt(0, takeReading, RISING); // Sample A0 on the rising edge of OC1A
// Initialise sample buffer
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0;
}
}
// Dust sample interrupt service routine
void takeReading() {
// Subtract the last reading:
total = total - readings[readIndex];
// Read from the sensor:
latest_reading = analogRead(dustPin);
readings[readIndex] = latest_reading;
// Add the reading to the total:
total = total + latest_reading;
// Advance to the next position in the array:
readIndex = readIndex + 1;
// If we're at the end of the array...wrap around to the beginning:
if (readIndex >= numReadings) readIndex = 0;
// Calculate the average:
average_reading = total / numReadings;
percent = ((average_reading * 100.0) / 1023); //% Dust in Air
dataReady = true;
}
void loop() {
uint16_t Dust_val1;
if (dataReady) {
// Critical section start
noInterrupts();
dataReady = false;
float tempPercent = percent;
// Critical section end
interrupts();
if (tempPercent > 0 && tempPercent <= 100.0) {
Dust_val1 = (uint16_t)(tempPercent * 10); //% Dust in Air*10
}
mySUART.flush();
uint8_t byte1 = Dust_val1 & 0xFF;
uint8_t byte2 = (Dust_val1 >> 8) & 0xFF;
mySUART.write(byte1);
mySUART.write(byte2);
delay(1000);
}
}
[/code]
This is the corrected code considering your remarks. Everything worked well for a few minutes, then the range of the variable changed again from 25 to 6530
Transmitting a variable initialized with a literal works correctly, and it is displayed within the normal range.
This is the corrected code considering your remarks.
You had printed the value being sent, maybe do that.
Or/and try just sending integers that don't come from any interrupt process, that is to say lose the interrupt thing for the moment and send, say, just an increasing number.
To see if it is the interrupt or the transmitting to blame.
Also if this test
if (tempPercent > 0 && tempPercent <= 100.0) {
fails, you are sending an uninitialised variabke, which can mean trouble or confusin at least. Why do you send if this test fails? Or you could the number sent.
a7
I didn't quite understand you. if (tempPercent > 0 && tempPercent <= 100.0) This condition is added to check the range. If I transmit a variable initialized with a literal, everything works fine. If I transmit a variable changing in the interrupt, after a few minutes it changes its range (25-6350, etc.). If I connect to the Arduino IDE on the receiver side through FOCA, the variable returns to the normal range.
uint16_t Dust_val1;
is an uninitialised local variable.
If the condition isn't met, you are using it as such. This is a Bad Idea, so just
uint16_t Dust_val1 = 0;
to stay out of any trouble using an uninitialised variabke might cause. Why do you send if the test fails? Is the value meaningful? Put the sending inside the same block.
First try transmitting a changing value that isn't produced by the interrupt:
static int countingUp;
uint16_t Dust_val1;
Dust_val1 = countingUp++;
Just lose the interrupt for this test. Don't attach the ISR. See if the problem is in the sending, srsly using an uninitialised variable is not good.
And again, print what you think you are sending when you do use the interrupt mechanism.
a7
Everything worked well for a few minutes, then the range of the variable changed again from 25 to 6530
My guess is that the RX is going out of synch with the TX as I described in Post #17. When that happens the code ends up combining the second byte of one transmitted uint16_t with the first byte of the next transmitted uint16_t. It's easy enough to check, print the individual byte values in the RX code to the Serial Monitor. Be sure to print a marker character or extra spaces of something after every two bytes of what you think is a received uint16_t. That way you'll see if the bytes are properly grouped together properly or not.
The ground is connected correctly. The power supply and RX/TX pins are fine. Additionally, the RX and TX pins are connected accordingly. The power supply's output matches the load. Besides, I tried using two independent power sources with a common ground. The variable is output to an independent display, not to the Serial monitor
So it’s working now ?
Your code has no synchronization to guarantee that the RX will receive the TX bytes in the correct order rather than than getting bytes from two different
uint16_tvalues sent from the TX.
You are absolutely right. The main reason is synchronization
[code]
#include <SoftwareSerial.h>
#include <TimerOne.h>
SoftwareSerial mySUART(7, 8); // D7, D8 = SRX, STX
const int numReadings = 100; // samples are taken at 100Hz so calculate average over 1sec
volatile int readings[numReadings]; // the readings from the analog input
volatile int readIndex = 0; // the index of the current reading
volatile long int total = 0; // the running total
volatile int latest_reading = 0; // the latest reading
volatile int average_reading = 0; // the average reading
int dustPin = A0;
float percent;
volatile bool dataReady = false;
void setup() {
//Serial.begin(9600);
mySUART.begin(9600);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(2, INPUT_PULLUP);
Timer1.initialize(10000);
Timer1.pwm(10, 991);
Timer1.pwm(9, 999);
attachInterrupt(0, takeReading, RISING);
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0;
}
}
void takeReading() {
total = total - readings[readIndex];
latest_reading = analogRead(dustPin);
readings[readIndex] = latest_reading;
total = total + latest_reading;
readIndex = readIndex + 1;
if (readIndex >= numReadings) readIndex = 0;
average_reading = total / numReadings;
percent = ((average_reading * 100.0) / 1023);
dataReady = true;
}
void loop() {
uint16_t Dust_val1;
if (dataReady) {
noInterrupts();
dataReady = false;
float tempPercent = percent;
interrupts();
if (tempPercent > 0 && tempPercent <= 100.0) {
Dust_val1 = (uint16_t)(tempPercent * 10);
} else {
Dust_val1 = 0; //
}
mySUART.flush();
uint8_t byte1 = Dust_val1 & 0xFF;
uint8_t byte2 = (Dust_val1 >> 8) & 0xFF;
mySUART.write(0xAA); // Start
mySUART.write(byte1);
mySUART.write(byte2);
mySUART.write(0xBA); // Stop
delay(1000);
}
}
[/code]
void Feedback_Dust_sensor() {
if (mySUART.available()){; // >= 4) {
if (mySUART.read() == 0xAA) { // start check
uint8_t receivedBytes[3];
mySUART.readBytes(receivedBytes, 3);
if (receivedBytes[2] == 0xBA) { // stop check
uint16_t receivedData = (receivedBytes[1] << 8) | receivedBytes[0];
dust_percent = receivedData; //% Dust *10
}
}
delay(5);
}
}
Thanks all for support. The main reason is synchronization
mySUART.write(0xAA); // Start mySUART.write(byte1); mySUART.write(byte2); mySUART.write(0xBA); // Stop
That's better but still not bullet proof. What will happen if 0xAA or 0xBA appear as one of the bytes in the data you are transferring?
If 0xAA or 0xBA appear as one of the bytes in the data you are transferring, they could be mistaken for the start and stop bytes.
This test code indicates that the issue is related to synchronization and nothing else. Next, I need to think about how to differentiate between data and control bytes. . But that's another question.
Next, I need to think about how to differentiate between data and control bytes.
I gave two suggestions in Post #17, one for ASCII transport and one for binary.
still not bullet proof
There is a technique called escape sequence used in protocols like HDLC , SLIP , PPP, X.25, ...
Say you take 0x7E as the header byte and thus you want to ensure it only appears in the byte stream as a start marker to guarantee you can be in sync.
It means that every occurrence of 0x7E in the payload must be replaced by something else, this is what is called an escape sequence. using an escape byte, let's say it's 0x7D.
➜ When encoding, if you encounter 0x7E in the payload, you replace it with 0x7D 0x5E, and if you encounter 0x7D (the escape byte itself), you replace it with 0x7D 0x5D.
The escaped sequence is built by prefixing the byte with 0x7D (the escape byte) and replacing it with the original byte XORed with 0x20. This transformation ensures that the escaped version of 0x7E (0x5E) and 0x7D (0x5D) do not conflict with control bytes while keeping the transformation reversible.
To revert, the receiver checks for 0x7D and when received, applies an XOR 0x20 to the next byte (or you can go the extra steps and check If the next byte is 0x5E, it restores 0x7E and if the next byte is 0x5D, it restores 0x7D the same way. If any other byte follows 0x7D , it indicates an error, as it violates the expected escape sequence pattern.)
This is pretty efficient as it requires very few extra bytes statistically and you only need to look at the next byte to make a decision so you don't add much latency when receiving the escape character and the XOR is fast.