Skip to content

Instantly share code, notes, and snippets.

@Electronza
Created December 16, 2019 11:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Electronza/8ea02d24f8d8c895abf58d89c24a4851 to your computer and use it in GitHub Desktop.
Save Electronza/8ea02d24f8d8c895abf58d89c24a4851 to your computer and use it in GitHub Desktop.
4-20mA calibrated receiver
/*******************************************************************
____ __ ____ ___ ____ ____ __ __ _ ____ __
( __)( ) ( __)/ __)(_ _)( _ \ / \ ( ( \(__ ) / _\
) _) / (_/\ ) _)( (__ )( ) /( O )/ / / _/ / \
(____)\____/(____)\___) (__) (__\_) \__/ \_)__)(____)\_/\_/
Project name:
Project page: https://electronza.com/4-20ma-current-loop-revisited-a-simpler-calibration-procedure
Description:
********************************************************************/
/*
4-20mA R click receiver
Reads result on 4-20mA bus
*/
#include <SPI.h>
// Arduino UNO with Mikroe Arduino Uno Click shield
// 4-20mA R click is placed in socket #2
// CS is pin 9
// SCK is pin 13
// MISO is pin 12
// MOSI is pin 11
#define ADC_CS 9
int loop_current;
int received_data;
// Calibration data obtained by running the calibration code
const int ADC_4mA = 784;
const int ADC_20mA = 3954;
// Data min and max range
// Matches the values on the transmitter code
// But it's a good ideea to resample to a lower resolution
const int data_min_range = 0;
const int data_max_range = 1023;
void setup() {
/* Resetting MCP3201
* From MCP3201 datasheet: If the device was powered up
* with the CS pin low, it must be brought high and back low
* to initiate communication.
* The device will begin to sample the analog
* input on the first rising edge after CS goes low. */
pinMode (ADC_CS, OUTPUT);
digitalWrite(ADC_CS, 0);
delay(100);
digitalWrite(ADC_CS, 1);
// initialize serial
Serial.begin(9600);
// initialize SPI
SPI.begin();
}
void loop() {
// Read the loop current
loop_current = ReadFrom420mA();
// Error checking
if (loop_current == -1)
Serial.println("Error: open loop");
else if (loop_current == -2)
Serial.println("Error: current loop is in short circuit");
// All is OK, remapping to initial data range
else {
received_data = map(loop_current, ADC_4mA, ADC_20mA, data_min_range, data_max_range);
Serial.print("Received value is: ");
Serial.println(received_data);
}
}
unsigned int get_ADC(void){
/*
DAC works on SPI
We receive 16 bits
Of which we extract only 12 bits
MCP3201 has a strange way of formatting data
with 5 bits in the first byte and
the rest of 7 bits in the second byte
*/
unsigned int result;
unsigned int first_byte;
unsigned int second_byte;
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
digitalWrite(ADC_CS, 0);
first_byte = SPI.transfer(0);
second_byte = SPI.transfer(0);
digitalWrite(ADC_CS, 1);
SPI.endTransaction();
/* After the second eight clocks have been
sent to the device, the MCU receive register
will contain the lowest order seven bits and
the B1 bit repeated as the A/D Converter has begun
to shift out LSB first data with the extra clock.
Typical procedure would then call for the lower order
byte of data to be shifted right by one bit
to remove the extra B1 bit.
See MCP3201 datasheet, page 15
*/
result = ((first_byte & 0x1F) << 8) | second_byte;
result = result >> 1;
return result;
}
int ReadFrom420mA(void)
{
int result;
int ADC_result;
float ADC_avrg = 0;
for (int i = 0; i < 100; i++){
ADC_result = get_ADC();
// Measure every 1ms
delay(1);
ADC_avrg = ADC_avrg + ADC_result;
}
result = (int)(ADC_avrg/100);
// now we do some shortcircuit and open loop checking
// open loop
if (result < (ADC_4mA - 50)){
return -1;
}
// shortcircuit
if (result > (ADC_20mA + 50)){
return -2;
}
// everything is OK
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment