ESP8266 (MicroPython): how to poll without locking? - microcontroller

I’m new to micro-controller programming and trying to take a temp and humidity reading from a DHT11 connected to my ESP8266 every 2 minutes. My initial attempt was a naive while loop with a sleep in each iteration... this locked the device but did take a reading every 2 minutes as expected. Obviously this is not a good approach, and I sense that I’m missing something fundamental in terms of how to program a continuous process on the ESP8266 with MicroPython. Any help would be greatly appreciated.

There is a Arduino example sketch which implements a solution to an analogous problem: "BlinkWithoutDelay". Although it's C++ as opposed to your python problem, the idea is still the same.
Instead of polling the sensor data and utime.sleep()-ing until the next read, we can just repeadetly check the current time. If the current time minus the last time we did something exceeds a certain interval time, we do that thing and remember the time that we did. Else, we just continue to do different stuff.
As in a micropython blog post, we can do:
import dht
import machine
import utime
d = dht.DHT11(machine.Pin(4))
# keep track of the last time we did something
last_measurement_ms = 0
# define in what intervals we want to do something
INTERVAL = 5000 # do something every 5000 ms
# main "loop".
while True:
# has enough time elapsed? we need to use ticks_diff here
# as the documentation states.
#(https://docs.micropython.org/en/latest/pyboard/library/utime.html)
if utime.ticks_diff(utime.ticks_ms(), last_measurement_ms) >= INTERVAL:
# yes, do a measurement now.
d.measure()
temp = d.temperature()
print("Temperature is %d." % (temp))
#save the current time.
last_measurement_ms = utime.ticks_ms()
# do the stuff you would do normally.
# this will be spammed, as there is nothing else to do
print("Normal loop")
Note that I don't have an actual ESP8266 with the micropython firmware on it to validate this, but you should get the general idea.

A different approach is to let the machine "deep-sleep" between readings.
Then, if you have the RST pin connected to Pin 16, it can automatically wake (and take the next measurement).
I've been doing this, and my main.py looks something like:
import wifi
import mqtt
import dht
import time
# This function will wait for the wifi to connect, or timeout
# after 30 seconds.
wifi.connect()
# This import/function inits the sensor, and gets data from it
result = dht.sensor.measure()
# In my case, I'm pushing the data to an MQTT broker - this
# function connects to the broker and sends the relevant data.
mqtt.send(result)
# We need to sleep here, otherwise our push to the broker may not
# complete before we deepsleep below.
time.sleep(5)
# This function will stop all processing, and then try to wake
# after the given number of microseconds. This value should be 2
# minutes.
esp.deepsleep(1000000 * 60 * 2)
Things to note.
You must have Pin 16 wired to the RST pin.
I've left out the wifi, dht and mqtt modules - they will vary according to your requirements (such as the credentials, or indeed if you are using something other than MQTT).
You must have the time.sleep() call, else the ESP8266 may sleep before the message is sent. It also gives some time to Ctrl-C when connected by serial port and stop the reboot loop.
Obviously, if you have other things you need to do on the device (other than just wake up, read and send data), then you'll want to do something else.

Related

Understanding SD card read sequence in SPI mode

I'm trying to understand how data is read from an SD card in SPI mode, down to the lowest level (because of an upcoming project of mine).
My setup:
Arduino with SD.h library connected to standard SD card breakout with a logic analyser connected to important pins.
What I know so far:
I went through the initialisation process and I totally understand it now, but when it tries to read I get a little lost;
After the card init is done, the program tries to read from the address 0 with command CMD17: and after this follows some data:
What is weird, is that I could not find what this section of data is representing, it does not appear anywhere in the hexdump of the whole SD card. Only on the second read from the Arduino, at address 0x2000 I get the starting data from the hex dump I'm expecting:
And the beginning of the data packet:
My card is 32G, which is probably SDHC. I should point out that I'm just observing what apparently works and I'm trying to figure out why is that.
My question is, what the data from address 0 meant and why the "start" is at address 0x2000, even if my hexdump shows this data at address 0 (sector 0).

how can I send signal to computer

greetings to each of you. I need your help. I am working on a project. I need that when a > is greater than 5, the arduino sends a signal to the computer and starts playing the video on the computer. my computer operation system is Windows11. I want play video on windows media player or anything. everytime my computer is open and video on media player ready to play. I don't know how to do it. first I connected the servo motor, the servo motor rotated 70 degrees and hit the computer's "space" key. now I want to develop it further. When it enters the "if" period, it will send a signal and start playing the video automatically.
You don't need to think so complicated. With Java, C# or Python, you can code scripts where you can read values and take actions over the serial port. For Arduino side use Serial.begin(9600); in the setup() and write your code in loop as:
while (a > 5 )
{
Serial.println(1);
}
While your a greater than 5 we send 1 value to serial port. Now I will continue to explain over python.
import serial
ser = serial.Serial('COM5')
ser.flushInput()
while True:
try:
ser_bytes = ser.readline()
decoded_bytes = float(ser_bytes[0:len(ser_bytes)-2].decode("utf-8"))
print(decoded_bytes)
except:
print("Keyboard Interrupt")
break
You do your test and write your code in try section.

how to use esp32 ulp interrupt pulse counter and periodic wake up deepsleep mode

I am trying to measure power usage using dds353 kWh meter. This meter has a pulse output. I am interested in using the esp32 since I can periodically send the data over the internet to nodered dashboard.I am also very interested in using the esp32 in low power mode and periodically wake up to send data over mqtt. I have tried out examples from github using espressif idf but I would not mind an arduino equivalent. I would like to do hardware interrupt which when one of the rtc gpio pin goes high a counter is incremented while a seperate timer interrupt run and occasionally wakes up the main xtensia cores which fetches data from the rtc and sends it over. I have looked at the pulse counter examples and with my limited knowledge can not tell if the interrupts are triggered when the ulp is in sleep mode or only when it is on. I would really be glad if someone would show me how to basically use the ulp for counting pulses even when it is sleep mode and periodically wake up the main cores. I am ok with IDF or arduino examples
If you want to count pulses while in deep sleep youuse the ULP. Code on the ULP continues to execute when the board wakes up and goes to normal power mode. So when it is awake, it will still run the counter on the ULP processor unless you stop the ULP periodic wake up timer, ULP will keep waking up and running while the main CPU is active.
As you gave already checked with this example , it should be pretty close to what you need. The only difference seems to be that the example is set to wake up after a given number of pulses, rather than a fixed amount of time. However it should be easy to change that, by enabling deep sleep wake up from timer.For the Arduino you could check Some additional info:
ULP doesn't have GPIO interrupts. So you use deep sleep wake stub (small piece of code which runs immediately after deep sleep, prior to loading application from flash into RAM) you can increment the pulse counter variable, and go to sleep again. This way you can get low power consumption (~5uA) between pulses and moderate power consumption while running the wake stub (around 13mA), for a very short time.
So its up to you to experiment with your specific scenario.
You can use Pulse Counter(PCNT) feature in ESP32 to count the number of pulse in background, Understanding by using same you can able to do some periodic wake-up and read the count.. Its also possible to configure event when number of counts reached certain threshold and had lot of options,
For get information and available Interfaces and API's for Pulse Counter(PCNT) please follow below link, https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/pcnt.html
Initially I faced lot of issue to make Pulse Counter(PCNT) work in Adrino IDE for ESP-32, After multiple attempt I make it working, And same sample code is uploaded in GitHub for reference. I have not use all the API's in the official documentation but but used few of them and are working..
I have created sample program for a water flow meter, there also we use to get pulse which needs to count to measure the water flow rate, understanding simile to kWh meter.
GitHub Sample code Path:- https://github.com/Embedded-Linux-Developement/Arduino_Sample_Programs/tree/main/ESP_32/Water_Flow_Pulse_counter_WithOut_Interrupt_Using_PCNT
I have not placing the code here, because its there in GitHub and not directly for the asked question, but simile one and can use it. Its a working code I tested in HW.
Hopes Its helpful,
Regards, Jerry James

How to use QTimer to get data from an external peripheral device for a specific time interval only?

Aim:
I am working on Qt Creator and want to interface Modem with my main program
, for that, I need to fetch the data from the modem for a specific time interval (say 3 seconds) only and after that I have to stop receiving data.
My Work:
I tried to implement it using:
QTimer::singleShot(3000,this,SLOT(SLOT1()));
But the limitation with it:
It calls the SLOT1 after 3000 m sec, but this is not what I want.
My requirement:
is to use timer in order to fetch data for 3 seconds only and then stop.

Passing data on to packet for transmission using readstream

I am using the readstream interface to sample at 100hz, I have been able to integrate the interface into Oscilloscope application. I just have a doubt in the way I pass on the buffer value on to the packet to be transmitted . Currently this is how I am doing it :
uint8_t i=0;
event void ReadStream.bufferDone( error_t result,uint16_t* buffer, uint16_t count )
{
if (reading < count )
i++;
local.readings[reading++] = buffer[i];
}
I have defined a buffer size of 50, I am not sure this is the way to do it as I am noticing just one sample per packet even though I have set Nreadings=2.
Also the sampling rate does not seem to be 100 samples/second when I check.I am not doing something right in the way I pass data to the packet to be transmitted.
I think I need to clarify a few things according to your questions and comments.
Reading a single sample from an accelerometer on micaZ motes works as follows:
Turn on the accelerometer.
Wait 17 milliseconds. According to the ADXL202E (the accelerometer) datasheet, startup time is 16.3 ms. This is because this particular hardware is capable of providing first reading not immediately after being powered on, but with some delay. If you decrease this delay, you will likely get a wrong reading, however, the behavior is undefined, so you may sometimes get a correct reading or the result may depend on environment conditions, such as ambient temperature. Changing this 17-ms delay to a lower value is certainly a bad idea.
Read values (in two axes) from the Analog to Digital Converter (ADC), which as an MCU component that converts analog output voltage of the accelerometer to the digital value (an integer). The speed at which ADC can sample is independent from the parameters of the accelerometer: it is another piece of hardware.
Turn off the accelerometer.
This is what happens when you call Read.read() in your code. You see that the maximum frequency at which you can sample is once every 17 ms, that is, 58 samples per second. It may be even a bit smaller because of some overhead from MCU or inaccuracy of timers. This is true when you sample by calling Read.read() in a loop or every fixed interval, because this call itself lasts no less than 17 ms (I mean the delay between the command and the event).
What you may want to do is:
Turn on the accelerometer.
Wait 17 ms.
Perform series of reads.
Turn off the accelerometer.
If you do so, you have one 17-ms delay for a set of samples instead of such delay for each sample. What is important, these steps have nothing to do with the interface you use for performing readings. You may call Read.read() multiple times in your application, however, it cannot be the same implementation of the read command that is already implemented for this accelerometer, because the existing implementation is responsible for turning on and off the accelerometer, and it waits 17 ms before reading each sample. For convenience, you may implement the ReadStream interface instead and call it once in your application.
Moreover, you wrote that ReadStream used a microsecond timer and is independent from the 17-ms settling time of the ADC. That sentence is completely wrong. First of all, you cannot say that an interface uses or does not use a timer. The interface is just a set of commands and events without their definitions. A particular implementation of the interface may use timers. The Read and ReadStream interfaces may be implemented multiple times on different platforms by various hardware components, such as accelerometers, thermometers, hygrometers, magnetometers, and so on. Secondly, the 17-ms settling time refers to the accelerometer, not the ADC. And no matter which interface you use, Read or ReadStream, and which timers a driver uses, milli- or microsecond, the 17-ms delay is always required after powering on the accelerometer. As I mentioned, you probably want to make this delay once per multiple reads instead of once per a single read.
It seems that the TinyOS source code already contains an implementation of the accelerometer driver providing the ReadStream interface which allows you to sample continuously. Look at the AccelXStreamC and AccelYStreamC components (in tos/sensorboards/mts300/).
The ReadStream interface consists of two commands. postBuffer(val_t *buf, uint16_t count) is called to provide a buffer for samples. In the accelerometer driver, val_t is defined as uint16_t. You may post multiple buffers, one by one. This command does not yet start sampling and filling buffers. For that purpose, there is a read(uint32_t usPeriod) command, which directs the device to start filling buffers by sampling with the specified period (in microseconds). When a buffer is full, you get an event bufferDone(error_t result, val_t *buf, uint16_t count) and a component starts filling a next buffer, if any. If there are no buffers left, you get additionally an event readDone(error_t result, uint32_t usActualPeriod), which passes to your application a parameter usActualPeriod, which indicates an actual sampling period and may be different (especially, higher) from a period you requested when calling read due to some hardware constraints.
So the solution is to use the ReadStream interface provided by AccelXStreamC and AccelYStreamC (or maybe some higher-level components that use them) and pass an expected period in microseconds to the read command. If the actual period is lower than one you expect, this means that sampling at higher rate is impossible either due to hardware constraints or because it was not implemented in the ADC driver. In the second case, you may try to fix the driver, although it requires good knowledge of low-level programming. The ADC driver source code for this platform is located in tos/chips/atm128/adc.

Resources