ESP8266 + Micropython: Why do I keep getting the same value with periodic i2c reads? - accelerometer

I am writing some simple code using MicroPython running on a Digistump Oak, which is basically an ESP8266 breakout board. I'm trying to understand the behavior that I see when performing periodic reads of the sensors via i2c.
The following code (which reads the value of the ACCEL_XOUT_H and ACCEL_XOUT_L registers) works just fine:
>>> from machine import Pin, I2C
>>> bus = I2C(scl=Pin(2), sda=Pin(0))
>>> while True:
... h, l = bus.readfrom_mem(0x68, 0x3b, 2)
... print (-((((h<<8)+l)^0xFFFF) + 1) if (h & (1<<7)) else (h<<8)+l)
(That print statement is just performing the conversion from two's complement.)
As expected, that prints out values from the accelerometer that change in approximately real time as I move the imu around.
But if I introduce a delay into the loop, such as...
>>> import time
>>> from machine import Pin, I2C
>>> bus = I2C(scl=Pin(2), sda=Pin(0))
>>> while True:
... h, l = bus.readfrom_mem(0x68, 0x3b, 2)
... print (-((((h<<8)+l)^0xFFFF) + 1) if (h & (1<<7)) else (h<<8)+l)
... time.sleep(1)
...I see some very strange behavior. The values returned by the i2c read operation continue to remain the same for many iterations after the imu has changed orientation. I'm at a loss as to what is going on here: the i2c read operations read from the registers on the imu, which according to the documentation are updated at the sampling rate, which in a default configuration is going to be 1Khz. I don't see anything in the code or data path that could be latching or caching these values somehow.
This is the documentation on the accelerometer registers, as found in the Register Map and Descriptions document:
These registers store the most recent accelerometer measurements.
Accelerometer measurements are written to these registers at the
Sample Rate as defined in Register 25.
The accelerometer measurement registers, along with the temperature
measurement registers, gyroscope measurement registers, and external
sensor data registers, are composed of two sets of registers: an
internal register set and a user-facing read register set. The data
within the accelerometer sensors’ internal register set is always
updated at the Sample Rate. Meanwhile, the user-facing read register
set duplicates the internal register set’s data values whenever the
serial interface is idle.
Since I'm sleeping between read calls, I'm pretty sure the i2c serial interface is idle by any definition, and I don't see anything else seems relevant to this behavior.
Do you have any suggestions as to what could be going on here?

Related

ESP32 i2c GY-906 0xFF 1037.55 response, temperature sensor

I'm trying to run the code below on an ESP32 TTGO T-display running micropython from loboris. (It's esp32 pre-loaded with display drivers for TTGO Display) I have attached a GY-906 temp sensor through i2c for testing. i2c.scan() finds it without issue on 0x5a [80] like it is supposed to, but when I request temperature data, the response is always 0xFF instead of proper temperature readings.
When I run the exact same code on a WeMos D1 (only difference is the pin numbers) I get temperature data returned. I am attaching both logic analyzer screenshots hoping someone can tell me what I need to do differently. Both are directly wired from 3.3, gnd, and the 2 i2c pins.
Things I have tried: adding pull up resistors to SDA, SLC (10k, 1k, 100). Switching to different i2c pins. Result seems to be the same. What am I missing? Is there supposed to be a resistor somewhere I don't know about? Other hardware? The screenshots make me think that the GY906 is responding, just the wrong response value.
Main Code
import temp_sensor
Pin = machine.Pin
I2C = machine.I2C
i2c = machine.I2C(0, scl=Pin(22), sda=Pin(21), freq=100000)
temp1 = temp_sensor.Temp.init(i2c)
print(temp1.read_object_temp())
time.sleep(1)
print(temp1.read_object_temp())
time.sleep(1)
print(temp1.read_object_temp())
time.sleep(1)
print(temp1.read_object_temp())
temp_sensor.py
import mlx90614 ##From https://github.com/mcauser/micropython-mlx90614
class Temp():
def init(i2c):
try:
sensor = mlx90614.MLX90614(i2c)
except:
print('couldnt connect to an i2c temp sensor')
sensor = False
else:
print('temp found')
#return sensor
finally:
return sensor
bad esp32 TTGO T-Display:
good 8266:
For anyone receiving 1037.55 responses from your gy-906 or MXL90614 sensor, that translates to 0xFF, 0xFF or all high (ones) from the sensor. This seems to happen when the sensor doesn't understand how to respond. (Thank you, #jasonharper for helping me understand this)
Here's how the math works:
The 0xFF, 0xFF in decimal is 65535.
The sensor resolution is 1/50 of
a degree which means you divide 65535 x 0.02 to convert to Kelvin, or
1310.7 (K)
Kelvin to Celsius (subtract 237.15) gets you 1037.55 C
Celsius to Fahrenheit gets you 1899.59 F
Bottom line, your sensor is hiccuping because it doesn't like the stop bit between the write and read, or you have a problem with your I2C bus, either the protocol is doing the request wrong or you have a cabling issue (length or wire gauge or connection, etc).
If it's the protocol like it was for me, see if anyone has updated the I2C system library recently and try a different version if possible.
I traced this issue down for days. Luckily I had a number of different MicroPython capable chips and was able to narrow it down to an old version of the machine.I2C library adding that stupid "stop" above.
I bought a $10 protocol analyzer on amazon to make that image above, and I tried loading the code on each of these: Wemos D1, HitLego ESP32S and TTGO T-Display. By trying the code on each, I was able to narrow it down to only the T-Display not working, which needed an custom old firmware version to get the ST7789 display working. The next step is to try to update and recompile the display library from loboris to work with the most recent Micropython firmware. If I make it work, I will reply below.

Delay interfacing Arduino and Simulink

I am trying to read data from potentionmeter using an arduino microcontroller (tried both arduino UNO, arduino FIO) and using serial communication interface it to Simulink (I tried baud rates ranging from 57600-921600).
Here is the Arduino source code:
/*
AnalogReadSerial
Reads an analog input on pin 0, prints the result to the serial monitor.
*/
#define ana_pin A0
void setup() {
analogReference(DEFAULT);
Serial.begin(57600);
}
// the loop routine runs over and over again forever:
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(ana_pin);
// print out the value you read:
Serial.print(sensorValue);
// delay(500); // delay in between reads for stability
}
I interfaced it with Tera Term software and there is instantaneous change of values corresponding to 3 V or 0V.
However when I tried to interface it with Simulink model using instrument control toolbox:
there is a 10 second lag when value changes from ASCII representation of 3V to 0V
The block sample time is 0.01 seconds and the model configuration parameters are adjusted accordingly (I tried it for 1 second or more and the delay still remains. Also, I was able to record data from another sensor and LPC1768 development board without any delay.
I also tried it with Arduino support libraries in Simulink:
And it seems that no data is received, as you can see from Scope1 in the png file the status signal is always 0. I have also attached hardware implementation properties from Arduino block in Simulink:
Could you help me understand what is going on and how to solve this issue?
#Patrick Trentin -
I get a delay of 4 seconds when I use baud rates of 230400, 460800 and 921600.
For baud rate of 57600, 115200 I get a delay of 10 seconds.
Thank you for pointing it out I did not focus on that previously.
However since I will be using the sensor in an application which accurately needs reading every 0.01 sec. I dont think I can work with 4 sec delay.

DAQ Matlab toolbox: how to count trigger events without an edge counter channel and how to output different value at each successive trigger

I need your help with the session based interface for the Matlab DAQ toolbox. I have not been able to find much help in the MathWorks tutorials or examples. I am currently using a USB-6003 DAQ from NI.
So basically in my system I have 2 analog output channels (ch1 and ch2) and 1 analog input channel (ch3), and what I am trying to do is to drive the output voltage in ch1 from 0V to 10V in steps of 1V, with ch2 constant and then repeat the loop in ch1 for a different voltage in ch2. As for the analog input ch3, I am triggering it some time after triggering the ch1. My triggers are being externally generated by a function generator.
What I have been struggling with is:
1) How to at each successive trigger event output a different value in the ch1.
2) And how after 11 triggers, can I change ch2 output's.
3) How to save the input in a different location between trigger events, so it does not get overwritten by the next event.
My main constraints are:
1) I cannot use an edge-counter channel to count the triggers because I only have two PFI channels and I need both, one to trigger ch1 and the other ch3 (I cannot use only one).
2) I cannot use wait or any other software time function, because I need a high speed acquisition system (it is for a laser microscope)
3) I need two have at least 2 sessions running in parallel because my DAQ does not allow simultaneous tasks in the same session.
I have attached a channel's time diagram of what I am trying to do.
Channels diagram
Caution
"I need a high speed acquisition system"
USB might not be the right option. Using USB as the control/data transport mechanism is slow compared to other computer I/O, like PCIe or EtherCAT. If, after you get this working, you determine that you need lower latency and jitter, my recommendation is to try CompactRIO and LabVIEW Real-Time.
Compounding the performance is the on-demand nature of the USB-6003. While both analong input and analog output are controlled by electrical signals (the start trigger and sample clock) and have their data automatically transferred by the driver, the digital input and counter are only software-timed, which means that reading data isn't automatic and must be prompted by you, the user, with a read command.
Since the only way you can get digital data from a USB-6003 is on-demand, your only option is to wait for it; there is no way to be notified that a new edge has arrived. Other devices (like the PCIe-63xx X Series or cDAQ-940x devices) support digital input change detection, which causes a software event to be sent to the program. If you had one of these devices, then you wouldn't have to wait.
Suggestion
However, if you change your triggering and data strategies a little, I still think you can achieve the kind of I/O you want. You'll then be able to evaluate its speed and reliability to decide if you need to upgrade DAQ hardware.
New triggering and data strategy
The core idea is: instead of keeping the channels on their own "time base", unify them to a single time base and use that to coordinate the voltage updates. By doubling the frequency of your external trigger, all three channels can share the same timing. In other words, both the analog input task and the analog output task use the same external signal as their sample clock.
Double the frequency of the FGEN's trigger signal.
Repeat an analog output sample if the level doesn't need to change.
Throw an analog input sample away if it coincides with an output level change.
The analog output samples would be:
ch1 ch2
0.0 0.0
0.0 0.0
1.0 0.0
1.0 0.0
2.0 0.0
2.0 0.0
0.0 1.0
0.0 1.0
New program strategy
Now that both the analog input and analog output are using the FGEN as their sample clock, the MATLAB routine only needs to prepare the operation and then monitor/feed it. The hardware will be able to generate and acquire without any intervention from the PC, but the PC will need to periodically read analog input data and write more analog output data to keep the driver satisfied.
I don't know how much of the DAQmx API MATLAB exposes, but you can ask the driver how many samples are left in the device's buffer
Analog input is DAQmxGetReadAvailSampPerChan (doc)
Analog output is DAQmxGetWriteSpaceAvail (doc)
Reference
NI USB-6003 Specifications
http://digital.ni.com/manuals.nsf/websearch/666A752FCC177B0186257CD8006C24C8

Understanding some ADC instructions

I am trying to program an adc in stm32f4. I want to know what are the roles of these five instructions?
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right
ADC_Mode_Independent The ADC functions independently of others. Other modes allow two ADC to be read at exactly the same time (for power measurement) or interleaved (2 or 3 ADC cooperate to read the same channel more often)
ADC_Prescaler_Div2 - How fast the ADC works its SAR algorithm
ADC_DMAAccessMode_Disabled - DMA provides the ability to take a number of readings and have them automatically put into a table in memory
ADC_TwoSamplingDelay_5Cycles - There are two registers, this one one is a delay between successive readings, the other is the sampling time, the time taken to physically sense the voltage on the pin. You must have a low impedance source to use shorter sampling. explained in the manual. Some processors can read the same pin more than once before stepping to the next pin, hence the delay read the ADC accuracy appnotes.
ADC_DataAlign_Right

Using MATLAB to send multiple serial signals through the same port

I'd like to send multiple signals (4 inputs and outputs and 7 outputs) from my Laptop to a microcontroller. I'm thinking of using a USB to serial converter and multiplexing the data through the port. I'll need to write codes both in the laptop end and in the microcontroller to multiplex the data.
Eg:
Tx of microcontroller:
1.Temperature sensor ADC output->Laptop
2.Voltage sensor to laptop
3.Current Sensor to Laptop
4.Photodiode current to Laptop
So I need to write a program in the microcontroller to send the data in this order. How can I accomplish this? I was thinking of an infinite loop which sends the data with time delays in between.
At the Rx pin of Microcontroller,
Seven bit sequences. Each bit sequence will be used to set the duty cycle of a PWM generated by the microcontroller.
I also need the same multiplexing or demultiplexing arrangement in the matlab end. Here too, I'm thinking of allotting some virtual 'channels' at different instants of time. What kind of algorithm would I need?
In case you always send all the inputs/outputs at the same rate, you could simply pack them into 'packets', which always start with one or more bytes with a fixed value that form a 'packet header'. The only risk is that one of the bytes of the sensor data might have the same value as the start-byte at the moment you try to start receiving bytes and you are not yet synchronized. You can reduce this risk by making the header longer, or by choosing a start-byte that is illegal output for the sensors (typically OxFF or so).
The sending loop on the microcontroller is really easy (pseudocode):
while True:
measure_sensors()
serial.send(START_BYTE)
serial.send(temperature)
serial.send(voltage)
serial.send(current)
serial.send(photodiode)
end while
The receiving loop is a bit more tricky, since it needs to synchronize first:
while True:
data = serial.receive()
if data != START_BYTE:
print 'not synced'
continue #restart at top of while
end if
temperature = serial.receive()
voltage = serial.receive()
current = serial.receive()
photodiode = serial.receive()
do_stuff_with_measurements()
end while
This same scheme can be used for communication in both directions.