Closed betyar closed 3 years ago
@betyar Not possible with one of the existing libraries. As the clock is 16x slower the minimum time micros can read is 64 us which would make it impossible to differentiate between a 0 and a 1 bit.
BUT there is always a but,
In itself it should be possible - thinking out loud
Another idea is to start from the example that does the pulse diagnose as it is pretty basic.
No beginner project but I expect it is doable in this way.
Q: Can you tell more about the project? Q: Why don't you use a DHT22? Q: Do you need to measure temp and humidity both? Q: What is your level of experience with hardware timers and registers etc ?
Many thanks Rob for your quick reply. The project I am working on is a small battery operated temperature and humidity sensor module that will take measurements once every half hour or hour and transmit the data via nRF24L01 for further processing. Currently I am planning on using it in small enclosed spaces such as a pantry or storage room, that is why it would be convenient if both temperature and humidity can be measured. For now I am using a DHT11 for prototyping and testing purposes since it is cheaper and still fairly accurate. After if it proves to be viable and works well, then I would use a DHT22 instead. Unfortunately my experience with hardware timers and registers is still at the beginner/intermediate stage, so I still have a lot to learn and try out. Your guidance and ideas are very helpful and I will look into them. Thanks for your help.
I have no setup for 1 MHz but I created a first code example you can try. It works nearly perfect with a DHT22 at 16 MHz UNO
Please try it first at 16 MHZ and let me know if and how it works
In line 10 change #define MHZ to 1 2 4 8 16 In line 11 change #define TYPE to 11 or 22
//
// FILE: DHT_1MHZ.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo DHT reading with timer1.
// DATE: 2021-01-11
// (c) : MIT
const int MHZ = 16; // 8 4 2 1 ?
const int TYPE = 22; // 11
//////////////////////////////////////
//
// TIMER 1 VAR
//
// overflow counter for timer
volatile uint32_t count = 0;
//////////////////////////////////////
//
// DHT11 SENSOR
//
int datapin = 10;
uint8_t bits[5];
float temperature = 0;
float humidity = 0;
//////////////////////////////////////
//
// SETUP & LOOP
//
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
setupTimer1();
}
void loop()
{
read(TYPE);
Serial.print(temperature, 1);
Serial.print('\t');
Serial.println(humidity, 1);
delay(2000);
}
////////////////////////////////////////////
//
// DHT11 core code - no error handling
//
bool read(uint8_t type)
{
// EMPTY BITS BUFFER
for (uint8_t i = 0; i < 5; i++) bits[i] = 0;
// READ THE BYTES
noInterrupts();
int rv = readSensor(type);
interrupts();
if (rv != 0)
{
Serial.print("Error: ");
Serial.println(rv);
}
// debugging
// for (int i = 0; i < 5; i++)
// {
// Serial.print(bits[i], HEX);
// Serial.print(' ');
// }
// Serial.println();
// Data-bus's free status is high voltage level.
pinMode(datapin, OUTPUT);
digitalWrite(datapin, HIGH);
// DECODE TEMPERATURE AND HUMIDITY
if (type == 22)
{
humidity = (bits[0] * 256 + bits[1]) * 0.1;
temperature = ((bits[2] & 0x7F) * 256 + bits[3]) * 0.1;
if (bits[2] & 0x80)
{
temperature = -temperature;
}
}
if (type == 11)
{
humidity = bits[0] + bits[1] * 0.1;
temperature = bits[2] + bits[3] * 0.1;
}
// TEST CHECKSUM
uint8_t sum = bits[0] + bits[1] + bits[2] + bits[3];
return (bits[4] != sum);
}
// LOW LEVEL CLOCKING IN BITS
int readSensor(uint8_t type)
{
// REQUEST SAMPLE - SEND WAKEUP TO SENSOR
pinMode(datapin, OUTPUT);
digitalWrite(datapin, LOW);
// WAIT DHT22 1 ms DHT11 18 ms
uint32_t start = timer1Micros();
if (type == 22) while (timer1Micros() - start < 1000);
if (type == 11) while (timer1Micros() - start < 18000);
// HOST GIVES CONTROL TO SENSOR
digitalWrite(datapin, HIGH);
if (MHZ == 1)
{
// delay 2 usec 1MHZ variant
asm("nop");
asm("nop");
}
else
{
delayMicroseconds(2);
}
pinMode(datapin, INPUT_PULLUP);
// SENSOR PULLS LOW IF WAKEUP
start = timer1Micros();
while (digitalRead(datapin) == HIGH)
{
if (timer1Micros() - start > 20000) return -1; // TIMEOUT
}
// SENSOR STAYS LOW for ~80 us
start = timer1Micros();
while (digitalRead(datapin) == LOW)
{
if (timer1Micros() - start > 100) return -2; // TIMEOUT
}
// SENSOR STAYS HIGH for ~80 us
start = timer1Micros();
while (digitalRead(datapin) == HIGH)
{
if (timer1Micros() - start > 100) return -3;
}
// SENSOR HAS NOW SEND ACKNOWLEDGE ON WAKEUP
// NOW IT SENDS THE BITS
// READ THE OUTPUT - 40 BITS => 5 BYTES
uint8_t mask = 0x80;
uint8_t idx = 0;
for (uint8_t i = 40; i != 0; i--)
{
while (digitalRead(datapin) == LOW);
// DURATION OF HIGH DETERMINES 0 or 1
// 26-28 us ==> 0
// 70 us ==> 1
start = timer1Micros();
while (digitalRead(datapin) == HIGH);
if (timer1Micros() - start > 50)
{
bits[idx] |= mask;
}
// PREPARE FOR NEXT BIT
mask >>= 1;
if (mask == 0) // next byte?
{
mask = 0x80;
idx++;
}
}
return 0;
}
////////////////////////////////////////////
//
// TIMER1 timing
//
uint32_t timer1Micros()
{
cli();
uint16_t v = TCNT1;
sei();
return (count * 65536UL + v) / MHZ;
}
void setupTimer1()
{
cli();
TCCR1A = 0;
TCCR1B = 0;
TIMSK1 = (1 << TOIE1); // Timer Overflow Interrupt Enable 1
TCCR1B |= (1 << CS10); // run at max speed
sei();
}
ISR(TIMER1_OVF_vect)
{
count++;
}
// -- END OF FILE --
updated the code to catch the first bug :)
Many thanks for the code. I will try it tomorrow (it's late here; BTW Greetings from Hungary).
OK good night from the Netherlands!
I did a small measurement @ 16 MHz, - inserted at the end of setup()
uint32_t start = timer1Micros();
pinMode(10, OUTPUT);
uint32_t dur = timer1Micros() - start;
Serial.println(dur);
delay(10);
start = timer1Micros();
digitalWrite(10, HIGH);
dur = timer1Micros() - start;
Serial.println(dur);
delay(10);
start = timer1Micros();
digitalWrite(10, LOW);
dur = timer1Micros() - start;
Serial.println(dur);
delay(10);
start = timer1Micros();
int x = digitalRead(10);
dur = timer1Micros() - start;
Serial.println(dur);
delay(10);
That means that these functions at 1 MHz take 16x as long so they are useless as the timing of the signals of the sensor is faster. So all these 3 functions should be rewritten in portmanipulation version.
https://www.arduino.cc/en/Reference/PortManipulation
To be continued.
I got it working @ 16 MHz with timer1 and port manipulations.
This should work at lower clocks, maybe even at 1 MHz Still have some ideas to tweak it more if needed.
//
// FILE: DHT_1MHZ.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo DHT reading with timer1.
// DATE: 2021-01-11
// (c) : MIT
// to squeeze timing even more (and make it even more UNO specific)
// https://www.arduino.cc/en/Reference/PortManipulation
const int MHZ = 16; // 8 4 2 1 ?
const int TYPE = 22; // 11
//////////////////////////////////////
//
// TIMER 1 VAR
//
// overflow counter for timer
volatile uint32_t count = 0;
//////////////////////////////////////
//
// DHT11 SENSOR
//
int datapin = 10; // port B mask = B00000100; // 0x04;
uint8_t bits[5];
float temperature = 0;
float humidity = 0;
//////////////////////////////////////
//
// SETUP & LOOP
//
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
setupTimer1();
// measure basic functions
uint32_t start = timer1Micros();
pinMode(10, OUTPUT);
uint32_t dur = timer1Micros() - start;
Serial.println(dur);
delay(10);
start = timer1Micros();
digitalWrite(10, HIGH);
dur = timer1Micros() - start;
Serial.println(dur);
delay(10);
start = timer1Micros();
digitalWrite(10, LOW);
dur = timer1Micros() - start;
Serial.println(dur);
delay(10);
start = timer1Micros();
int x = digitalRead(10);
dur = timer1Micros() - start;
Serial.println(dur);
delay(10);
}
void loop()
{
read(TYPE);
Serial.print(temperature, 1);
Serial.print('\t');
Serial.println(humidity, 1);
delay(2000);
}
////////////////////////////////////////////
//
// DHT11 core code - no error handling
//
bool read(uint8_t type)
{
// EMPTY BITS BUFFER
for (uint8_t i = 0; i < 5; i++) bits[i] = 0;
// READ THE BYTES
noInterrupts();
int rv = readSensor(type);
interrupts();
if (rv != 0)
{
Serial.print("Error: ");
Serial.println(rv);
}
// debugging
// for (int i = 0; i < 5; i++)
// {
// Serial.print(bits[i], HEX);
// Serial.print(' ');
// }
// Serial.println();
// Data-bus's free status is high voltage level.
// pinMode(datapin, OUTPUT);
DDRB |= B00000100;
// digitalWrite(datapin, HIGH);
PORTB |= B00000100;
// DECODE TEMPERATURE AND HUMIDITY
if (type == 22)
{
humidity = (bits[0] * 256 + bits[1]) * 0.1;
temperature = ((bits[2] & 0x7F) * 256 + bits[3]) * 0.1;
if (bits[2] & 0x80)
{
temperature = -temperature;
}
}
if (type == 11)
{
humidity = bits[0] + bits[1] * 0.1;
temperature = bits[2] + bits[3] * 0.1;
}
// TEST CHECKSUM
uint8_t sum = bits[0] + bits[1] + bits[2] + bits[3];
return (bits[4] != sum);
}
// LOW LEVEL CLOCKING IN BITS
int readSensor(uint8_t type)
{
// REQUEST SAMPLE - SEND WAKEUP TO SENSOR
// pinMode(datapin, OUTPUT);
DDRB |= B00000100;
// digitalWrite(datapin, LOW);
PORTB &= B11111011;
// WAIT DHT22 1 ms DHT11 18 ms
uint32_t start = timer1Micros();
if (type == 22) while (timer1Micros() - start < 1000);
if (type == 11) while (timer1Micros() - start < 18000);
// HOST GIVES CONTROL TO SENSOR
// digitalWrite(datapin, HIGH);
PORTB |= B00000100;
if (MHZ == 1)
{
// delay 2 usec 1MHZ variant
asm("nop");
asm("nop");
}
else
{
delayMicroseconds(2);
}
// pinMode(datapin, INPUT_PULLUP);
DDRB = DDRB & B11111011;
// SENSOR PULLS LOW IF WAKEUP
start = timer1Micros();
// while (digitalRead(datapin) == HIGH)
while (PINB & B00000100)
{
if (timer1Micros() - start > 25000) return -1; // TIMEOUT
}
// SENSOR STAYS LOW for ~80 us
start = timer1Micros();
// while (digitalRead(datapin) == LOW)
while (!(PINB & B00000100))
{
if (timer1Micros() - start > 100) return -2; // TIMEOUT
}
// SENSOR STAYS HIGH for ~80 us
start = timer1Micros();
// while (digitalRead(datapin) == HIGH)
while (PINB & B00000100)
{
if (timer1Micros() - start > 100) return -3;
}
// SENSOR HAS NOW SEND ACKNOWLEDGE ON WAKEUP
// NOW IT SENDS THE BITS
// READ THE OUTPUT - 40 BITS => 5 BYTES
uint8_t mask = 0x80;
uint8_t idx = 0;
for (uint8_t i = 40; i != 0; i--)
{
// while (digitalRead(datapin) == LOW);
while (!(PINB & B00000100));
// DURATION OF HIGH DETERMINES 0 or 1
// 26-28 us ==> 0
// 70 us ==> 1
start = timer1Micros();
// while (digitalRead(datapin) == HIGH);
while (PINB & B00000100);
if (timer1Micros() - start > 50)
{
bits[idx] |= mask;
}
// PREPARE FOR NEXT BIT
mask >>= 1;
if (mask == 0) // next byte?
{
mask = 0x80;
idx++;
}
}
return 0;
}
////////////////////////////////////////////
//
// TIMER1 timing
//
uint32_t timer1Micros()
{
cli();
uint16_t v = TCNT1;
sei();
return (count * 65536UL + v) / MHZ;
}
void setupTimer1()
{
cli();
TCCR1A = 0;
TCCR1B = 0;
TIMSK1 = (1 << TOIE1); // Timer Overflow Interrupt Enable 1
TCCR1B |= (1 << CS10); // run at max speed
sei();
}
ISR(TIMER1_OVF_vect)
{
count++;
}
// -- END OF FILE --
Did you read - http://www.gammon.com.au/power by the way?
Another way to safe power is to make the VCC line of the DHT11 sensor under program control, (use a mosfet in between, do not directly feed the sensor from an IO pin.)
Did some tests. Some good news.
At 16 MHz works as expected. There are errors at the beginning (Error: -1 0 0) about 7 but then proceeds smoothly. Interestingly, when I use a mofset then there are always 10 errors at the beginning as opposed to the usual 7 without a mofset.
At 8 Mhz works the same as at 16 MHz. There are, however, a couple of additional errors after the usual ones at the beginning but they are few and far between.
At 1 Mhz it generally works. Errors are not reported but the transmission of the data is erratic. Here is the terminal result at 1 MHz:
16.1⸮5⸮.⸮⸮
16.1 49.⸮⸮⸮16.1⸮49.⸮⸮⸮16.2⸮49.⸮
16.2⸮49.⸮
⸮16.2 49.⸮⸮⸮16.1⸮49.⸮⸮⸮16.2⸮49.⸮⸮⸮16.2⸮49.⸮⸮⸮16.2⸮49.⸮⸮⸮16.2 48.⸮
16.2⸮48.⸮⸮
16.2⸮48.⸮
16.2 48.⸮
⸮16.2⸮48.⸮
16.2⸮48.⸮
16.2⸮48.⸮
⸮16.2⸮48.⸮⸮
16.2⸮48.⸮⸮
16.2⸮48.⸮⸮⸮16.2⸮48.⸮⸮
16.3⸮48.⸮⸮⸮16.2⸮48.⸮⸮⸮16.2 48.⸮⸮⸮16.3 48.⸮⸮⸮16.2⸮49.⸮⸮
16.2⸮48.⸮⸮⸮16.2⸮48.⸮⸮⸮16.3⸮48.⸮⸮
16.3⸮48.⸮
⸮16.4⸮48.⸮⸮⸮16.3⸮48.⸮⸮⸮16.3⸮48.⸮⸮
16.4 48.⸮⸮⸮16.3 48.⸮
⸮16.4⸮48.⸮⸮
16.4⸮48.⸮⸮⸮16.3⸮48.⸮⸮
16.4⸮48.⸮⸮
16.3 48.⸮
⸮16.3⸮47.⸮⸮⸮16.4⸮48.⸮
16.3⸮48.⸮⸮⸮16.4⸮47.⸮
⸮16.3⸮48.⸮⸮⸮16.3⸮47.⸮
16.4⸮48.⸮
16.3⸮47.⸮⸮⸮16.3⸮47.⸮⸮⸮16.4 4⸮.⸮⸮
16.3 48.⸮⸮⸮16.3 48.⸮
16.3⸮48.⸮
⸮16.4⸮47.⸮⸮⸮16.4⸮47.⸮
⸮16.4 47.⸮⸮⸮16.3⸮4⸮.⸮⸮⸮16.4 48.⸮⸮⸮16.4⸮47.⸮⸮⸮16.4⸮47.⸮
⸮16.3⸮4⸮.⸮⸮⸮16.3⸮47.⸮⸮⸮16.3⸮48.⸮⸮⸮16.3 48.⸮⸮⸮16.4⸮48.⸮⸮⸮16.3⸮47.⸮⸮⸮16.3⸮47.⸮⸮⸮16.3⸮47.⸮⸮⸮16.3⸮48.⸮⸮⸮16.3⸮48.⸮⸮
16.4⸮48.⸮
16.4⸮48.⸮
⸮16.4⸮47.⸮
16.3⸮47.⸮⸮⸮16.3⸮47.⸮⸮⸮16.3 48.⸮⸮⸮16.3⸮48.⸮
16.4⸮48.⸮⸮⸮16.4⸮47.⸮
⸮16.3⸮47.⸮⸮
16.4⸮47.⸮⸮
16.3 47.⸮⸮⸮16.3⸮48.⸮⸮
16.3 48.⸮
⸮
Generally you have proved that it can be done, I just now need to work a little on fine tuning. This is especially so in my case, because in my project the CPU goes to sleep for an extended period of time and then wakes up to take only a reading or two before it sleeps again. Ironically, the error message doesn't appear
By the way, the actual temperature and humidity readings are quite accurate for a DHT11.
Great, good that it works. How do you handle baudrate?
I see special characters "⸮⸮⸮" popping up I assume these are \t an \n
There are errors at the beginning (Error: -1 0 0) about 7 but then proceeds smoothly.
The sensor has a power up delay, that might be a few seconds. check datasheet
Sorry, had to pop put for a few minutes. I did not change anything in the code, the terminal baud rate was set to 115200. No doubt I will have to look into this as well as test it with the transceiver. For now I just did a simple test with the CPU, the sensor, and with/without the mofset. I will definitely check with the datasheet about the delay, I think it was 300 ms but I am not sure.
The hex for the special characters are the following: E2 B8 AE. They always followed in this order. The carriage return (0D) was not as frequent. Also at the very beginning was EF BB BF but that could be simply related to powering up.
Quick math would say that 115200 baud would be 115200 / 16 = 7200 baud. Maybe a program like teraterm is better capable handling this unusual baudrate.
The hex for the special characters are the following: E2 B8 AE. They always followed in this order. The carriage return (0D) was not as frequent. Also at the very beginning was EF BB BF but that could be simply related to powering up.
OK in powering up there might be some garbage (not too unusual)
What is expected is (excluding errors)
Serial.print(temperature, 1); // digit digit point digit
Serial.print('\t'); // tab
Serial.println(humidity, 1); // digit digit point digit newline
so EF BB BF are pretty unexpected...
Apologies once again for being late in replying. I have been trying out different scenarios and the most optimal I came up with was a baud rate of 19200; no errors and no garbage and the reading displayed accurately one line after the other. This was tested without a mofset. Now I will have the big task to try and integrate this with my code which includes a watchdog as well as the transmitter. Many thanks for your instructive help. I felt I learned quite a bit from it.
No need to apologize, you probably have more things to do than only this project. Just like most of the people.
What should be possible with the baud rate is that you set it to 16x 19200 = 307200 The Arduino can send on any baudrate. As clocks are 16x slower this might work.
If the merging causes problems just let me know. Success!
Thanks again. I'll reopen if I have any further issues. All the best and keep up the great work. You're a great inspiration.
Please let me know how things work in the end!
Is it possible to somehow read a DHT11 with an ATMEGA328P running at 1 MHz? The reason the CPU is running so slow is because this is a battery operated project, thus I am trying to bring down current consumption to as low as possible. I tried using the minimum example and naturally received -999. I realize that the sensor is running too fast for the CPU at this rate, but is there some kind of trick to nevertheless capture the sensor readings?