4-20mA R click notes
Jump to navigation
Jump to search
4-20mA R click is a board that helps read out sensors that output 4-20mA current loop signals.
(there is a also a T click to transmit such signals)
Board has:
- INA196 - current shunt monitor
- measures current on the loop, outputs a voltage signal and feeds it to the ADC
- MCP3204 - 12-bit ADC with SPI interface
- can talk no faster than 1MHz (if on 3.3V supply)
- MAX6101EUR - 2.048V reference (for ADC)
- TPS61041 - boost converter, creates the ~15V for the loop (at sufficient current)
- its EN pin is on the microBUS Int line - lets you control when the sensor gets loop power (verify)
- SMD jumper selects whether board's Vcc comes from the MicroBUS 3.3 or 5V line.
- LED (directly on VCC)
Using / code
/* Interfaces with a 4-20mA R click, on an Arduino socket-shield where:
EN/INT is PD2 (D2)
CS is PB2 (D10)
MISO is PB4 (D12)
SCK is PB5 (D13)
In other words, the standard SPI pins, and D2 for enable (also fairly regular).
To read out the MCP3201 ADC:
You only need CS and MISO - output is triggered by device selection.
CS should by default be high. To read out, assert CS low.
The ADC will sample (two clock cycles), and output the bits on the successive clock signals
(in fact, it will write more than the sample, which is why we care only about the first 12 bits)
Readout code taken from http://www.speedlimit88.com/arduino/spi_adc/
In the case this was made for, the sensor is a portable level readout on a liquid nitrogen dewar,
and the actual readout is battery-powered, so we only want to trigger readout on explicit request.
Seen from our side, readout is triggered when we put voltage on the loop, a.k.a. assert EN/INT.
So assert EN, read out the ADC a few times, clean EN, and wait a while.
The "On request" part is implemented by "when we hear _anything_ on the serial port"
(which isn't ideal when the serial port sees reason to receive garbage).
*/
#include <SPI.h>
const int en_pin = 2;
const int select_adc_pin = 10;
const int ewma_amount = 250; // more samples of the loop current makes the result (look) more stable.
const float ewma_alpha = 0.01; // EWMA lowpass. Lower adapts slower.
const float om_ewma_alpha = (1.0-ewma_alpha);
// some specific-case tweaking of the following two can help
const float empty_level = 800;
const float full_level = 4000;
void eat_serial(byte amount=50) {
// Consume whatever's in the buffer. (and don't do so indefinitely)
byte count=amount;
while ((count>0) && (Serial.available()>0)) {
Serial.read();
count--;
}
}
unsigned int readout_mcp3201() { // Do a single readout
byte inByte = 0;
unsigned int result = 0;
digitalWrite(select_adc_pin, LOW);
result = SPI.transfer(0x00);
result = result << 8;
inByte = SPI.transfer(0x00);
result = result | inByte;
digitalWrite(select_adc_pin, HIGH);
result = result >> 1;
result = result & 0b0000111111111111;
return result;
}
void setup() {
Serial.begin(9600);
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV4);
pinMode(en_pin, OUTPUT);
digitalWrite(en_pin, LOW); // Don't power
pinMode(select_adc_pin, OUTPUT);
digitalWrite(select_adc_pin, HIGH); // Don't select
eat_serial();
delay(50); // probably nothing needs any delay, but we wouldn't notice this anyway.
}
void loop() {
unsigned int single_sample;
float temp_intermediate=0.0;
float temp_frac;
//Don't do anything until the serial link says something.
while (Serial.read()==-1) {
Serial.println("Power saving mode. Say something over the serial port to sample the dewar.");
delay(1000);
// TODO: flush the full buffer, so that we read only once.
}
eat_serial(); // Okay, the serial link sent something. Read it all, so we only react with one sampling.
digitalWrite(en_pin, HIGH); // Put current on the loop so that the dewar samples
delay(500); // TODO: figure out how short this can be.
temp_intermediate = (float)readout_mcp3201(); // first sample
for (int i=0;i<ewma_amount;i++) { // successive samples
single_sample = readout_mcp3201();
temp_intermediate = ewma_alpha*single_sample + om_ewma_alpha*temp_intermediate;
//Serial.print(single_sample);
//Serial.print(" ");
}
digitalWrite(en_pin, LOW);
//Serial.println("\nReadout (smoothed but not yet scaled): "); Serial.println(temp_intermediate);
if (temp_intermediate<750) {
Serial.println("No sensor connection");
} else {
temp_frac = (temp_intermediate-empty_level)/(full_level-empty_level);
//Serial.print( 20.0*temp_frac ); Serial.print("mA, ");
Serial.print( 100.0*temp_frac ); Serial.println("%");
}
delay(50); //can be short/removed when the "wait for serial talk" is in there
}