Is there a way to use some kind of ready flag instead of freeze for 500-1000millis with adafruit sht 4x lib?
Can't speak to the library but according to the SHT41 datasheet:
In case the sensor receives a read header and is still busy with e.g. measurement or heating, it will return a NACK.
So worst case it sounds like you could roll you own optimized "give me that reading the instant you've got it" interface.
I hoped that there is one inside adafruit and I didn't see it (:
Response time (63% of target) of this sensor is 4 seconds.
I don't see a point in reading it faster than that.
What do you mean with "freeze". Blocking code?
Leo..
Yes. It's blocking. I don't mind it update measure for a few sec, but I don't like it blocking me.
There is schematic image below in eng.
Honestly, at this point for no reason. Just trying to make loop as fast as possible.
Right now telegram update is blocking for 200-500ms while sending message. And I don't think it could possible be faster.
But one sensor blocking for 500+ms is annoying.
Only ask for a reading from the Sht41 every minute or so, the blocking will not be as predominant.
Tom..
Already do that) it still annoying)
I optimize code at this point to:
8.000 loops per second.
But max loop length is 1700ms. And I want to shorten max loop length at this point. And sht41 is part of the problem with blocking code.
The limit I see - is telegram sending message time. And it's ~500ms. So I want every other function to work faster than 500ms.
So, here we are... I did rewrite my code and it almost non blocking at this point
Average loop length for 20sec measure = 0.1ms
Max loop length = 1700ms.
You could restructure your code as a finite state machine and separate the initialization of the sensor from reading it instead of the current single activity of initialising the sensor, waiting Xms, then reading it. The code would then be non-blocking.
The data sheet has this pseudo code:
i2c_write(i2c_addr=0x44, tx_bytes=[0xFD])
wait_seconds(0.01)
rx_bytes = i2c_read(i2c_addr=0x44, number_of_bytes=6)
t_ticks = rx_bytes[0] * 256 + rx_bytes[1]
checksum_t = rx_bytes[2]
rh_ticks = rx_bytes[3] * 256 + rx_bytes[4]
checksum_rh = rx_bytes[5]
t_degC = -45 + 175 * t_ticks/65535
rh_pRH = -6 + 125 * rh_ticks/65535
if (rh_pRH > 100):
rh_pRH = 100
if (rh_pRH < 0):
rh_pRH = 0
Could you please explain me the basics of i2c? I suspect it doesn't have buffer like serial.
So, if I tell sensor to measure, that it send data back one time itself, or it send back data with second command witch can be in some range after first one?
One of the best references for I2C is here: Gammon Forum : Electronics : Microprocessors : I2C - Two-Wire Peripheral Interface - for Arduino by @nickgammon
However, you don't need to know much of the background details of I2C to use it.
In direct answer to your question it does have a buffer (32 bytes on say an Arduino Uno for the master).
I was suggesting that you split the activity into two separate transactions.
Transaction 1 is that the the master intitialises the slave and instructs it to prepare the data when it is ready and hold it in its internal buffer.
That is this part: i2c_write(i2c_addr=0x44, tx_bytes=[0xFD])
Transaction 2 performed Xms later is for the master to request the slave's internal buffer data and interpret the result. That is the code segment beginning rx_bytes = i2c_read(i2c_addr=0x44, number_of_bytes=6)
To wrap it in a simple state machine the code will be something like this which does not block the loop() either between measurements or in the interval between an initialisation and a read of the sensor:
enum state_t { WAITING_INIT, WAITING_READ } ;
state_t state = WAITING_INIT ;
uint32_t inStateAtMs = 0 ;
. . .
. . .
void loop() {
switch( state ) {
case WAITING_INIT :
if ( millis() - inStateAtMs > 20000UL ) { // say 20 seconds interval between measurements
// sensor initialisation with i2c read
state = WAITING_READ ;
inStateAtMs = millis() ;
break ;
}
case WAITING_READ :
if ( millis() - inStateAtMs > 500UL ) { // say 500ms after initialisation
// sensor read with i2c read then interpret result
state = WAITING_INIT ;
inStateAtMs = millis() ;
break ;
}
} // switch
. . .
other code in loop
. . .
} // loop
I2C is serial. You can read the chars 1 at a time and process each on arrival without blocking. Even 1700 microseconds is a long wait once you do it that way.
This functionality is a tool to develop non-blocking code. It lets you see the effect of code changes. A default ADC read takes 105 micros and I regard that as a long time for loop() while most others consider 1 ms acceptable. What I like to see is the counter showing 50+ KHz average, highly responsive. With 1 ADC read per 1 ms or 10 ms the average can still be high, using timers it is controllable to not hog cycles.
// NoBlockLoopCounterLT Mar 16, 2024 by GoForSmoke @ Arduino.cc Forum
// Some speed changes thanks to J-M-L Jackson of the Arduino forum!
// 3/16/24 -- another change from 777 of the Arduino forum uses timer0_millis.
// Free for use. Compiled on Arduino IDE 1.8.19
// This sketch counts times that loop has run each second and prints it.
// It uses the void LoopCounter() function that does not block other code.
// The LT is for Lighter Timing. Instead pf 32-bit subtraction we take
// advantage of what makes the low 8 bits of 32-bit millis() +/-1.
// The low 8 skips 6 values counting to 256, it takes 250ms to flip bit 8.
// Using 16 bits of millis() allows solid timing of 1/4 sec to 32 sec.
// I keep 1 byte to hold the ON/OFF of the last read of bit 10 = 1 sec.
// On an 8-bit CPU it should take fewer cycles.
// Please note that optimizations from J-M-L Jackson and 777 have been added.
// run this sketch just to see what such a lightweight sketch can run at
// ~370033 loops per second!
extern volatile unsigned long timer0_millis; // might be faster than millis()
void setup()
{
Serial.begin( 115200 );
Serial.println( F( "\n\n\n Loop Counter, free by GoForSmoke\n" ));
Serial.println( F( "This sketch counts times that loop has run each second and prints it." ));
}
void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
static unsigned long count; // only this function sees this
static bool lastBit10Set; // only this function sees this
word millis16 = timer0_millis;
count++; // adds 1 to count after any use in an expression, here it just adds 1.
bool currentBit10Set = millis16 & 0x0400; // leverage integral to bool implicit promotion
if (currentBit10Set != lastBit10Set) // 1 second
{
// Serial.print( millis16 ); // 16-bit binary into decimal text, many micros
// Serial.write('\t');
Serial.println( count ); // 32-bit binary into decimal text, load of cycles!
count = 0; // don't forget to reset the counter
lastBit10Set = currentBit10Set; // changes 0<==>1
}
}
void loop() // runs over and over, see how often with LoopCounter()
{
LoopCounter(); // the function runs as a task, the optimizer will inline the code.
}
somehow i did mistake witch sensor is blocking... idk how... It's now SHT41 it's ds18b20...
but thank you all for explaining i2c basics!
Which library are you using? If you are using the Dallas Temperature library, the default read of the sensor is blocking, but you can set it to not block. Instead you initiate a read which returns immediatly, save the current value of millis(), then after a specific time has elapsed read the data from the sensor. Wait time is 750mS for 12 bit resolution, down to 93.75ms for 9 bit resolution.
The library has two examples, WaitForConversion and WaitForConversion2, showing how to do this.
yeah, already found it... rewriting code in this moment)
Yeap, did non-blocking for ds18b20
now the max loop length is about 500ms when sending message to telegram.
and 8000-15000 loops per second average.
in setup()
sensors.setWaitForConversion(false);
Is it possible to have a 50khz loop (20microsec) with esp8266 and wifi connection?