After I moved to Victoria, I met someone who asked me to help him to build a quality assurance station to weigh the end product and kick out if the weight of the product is not in between the desired range. I wasn’t sure if I would be able to build it but I still accepted the task.
As usual I started with an arduino board and researched about the weigh sensors. At first, I wanted to use an arduino pro mini, but for easier programming purposes, I decided to use an arduino nano. Here is how it should work:
- We get the weight from the load sensor
- Then we convert the weight to voltage. We convert the information to voltage, because everything else is going to be handled by the PCB.
There were not many options for a weigh sensor, so I ended up using the “Digital Load Cell Weight Sensor HX711 AD Converter”.
I simply installed the HX711 library from the Arduino IDE library, yo ucan see it’s details here: bogde/HX711
Since the system’s main controller was a PLC, I was asked to convert the weight to a voltage range. So I found MCP4822 chip which was a great choice to output a voltage in between 0-4096 mV. The rest was handled by the PLC programming.
I found this example online for the voltage convertor, it explained every detail about the process, but I simply used their code, thanks to mr. Mohamad.
I used Fritzing for the development
Then I soldered them up on a simple board manually. And of course built a simple box with OpenSCAD and printed it out. This was also a good opportunity for me to test the Resin Printer I recently got.
We tested on the site and it was successful. So I decided to get the PCB
They sent it pretty fast
I will share the code here as well, so you can use the same
/*
D. Mohamad 29/03/17 code to commumincate with MCP4822 12-bit DAC via SPI
pin assignments as follows...
Uno (master) MCP4822 (slave)
CS 8 2
MOSI 11 4
SCK 13 3
Read output voltage with a multimeter Va/Vb on pin 8/6.
send commands via serial interface eg.
AL1000 would mean...
channel=A
gain=low
D=1000
Va = gain*Vref*D/4095
= 1*2.04*1000/4095 = 0.498V
see www.theonlineshed.com
*/
/* you need to send a message like ah100 in serial monitoring to test */
#include <SPI.h>
#include "HX711.h"
const int DAC_CS = 8; //Chip select pin for the DAC
String inputString = ""; //holds serial commands
// HX711 circuit wiring
const int LOADCELL_DOUT_PIN = 2;
const int LOADCELL_SCK_PIN = 3;
int can_weight = 0;
int min_weight = 0;
int max_weight = 650;
int min_milivolts = 0;
int max_milivolts = 4096;
int output_voltage = 0;
int weight_to_voltage_ratio = (max_milivolts-min_milivolts)/(max_weight-min_weight);
HX711 scale;
void setup() {
// set the CS as an output:
pinMode (DAC_CS, OUTPUT);
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
inputString.reserve(200); // reserve 200 bytes for the inputString:
SPI.begin();
Serial.println("Initializing the scale");
// Initialize library with data output pin, clock input pin and gain factor.
// Channel selection is made by passing the appropriate gain:
// - With a gain factor of 64 or 128, channel A is selected
// - With a gain factor of 32, channel B is selected
// By omitting the gain factor parameter, the library
// default "128" (Channel A) is used here.
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
Serial.println("Before setting up the scale:");
Serial.print("read: \t\t");
Serial.println(scale.read()); // print a raw reading from the ADC
Serial.print("read average: \t\t");
Serial.println(scale.read_average(20)); // print the average of 20 readings from the ADC
Serial.print("get value: \t\t");
Serial.println(scale.get_value(5)); // print the average of 5 readings from the ADC minus the tare weight (not set yet)
Serial.print("get units: \t\t");
Serial.println(scale.get_units(5), 1); // print the average of 5 readings from the ADC minus tare weight (not set) divided
// by the SCALE parameter (not set yet)
scale.set_scale(856.f); // CHANGE THIS VALUE TO CALIBRATE! this value is obtained by calibrating the scale with known weights; see the README for details
scale.tare(); // reset the scale to 0
Serial.println("After setting up the scale:");
Serial.print("read: \t\t");
Serial.println(scale.read()); // print a raw reading from the ADC
Serial.print("read average: \t\t");
Serial.println(scale.read_average(20)); // print the average of 20 readings from the ADC
Serial.print("get value: \t\t");
Serial.println(scale.get_value(5)); // print the average of 5 readings from the ADC minus the tare weight, set with tare()
Serial.print("get units: \t\t");
Serial.println(scale.get_units(5), 1); // print the average of 5 readings from the ADC minus tare weight, divided
// by the SCALE parameter set with set_scale
Serial.println("Readings:");
}
// lets convert the weigh into milivolts. it should be around 0mV - 4096mV. The weigh range is 0 - 650g.
void loop() {
unsigned int n; //number 2 bytes long unsigned
char channel, gain;
String errmsg; //errors returned from DAC_set
Serial.print("one reading:\t");
Serial.print(scale.get_units(), 1);
Serial.print("\t| average:\t");
Serial.println(scale.get_units(3), 1);
can_weight = scale.get_units(3);
output_voltage = weight_to_voltage_ratio*can_weight;
channel = "a";
gain = "h";
errmsg="";
DAC_set(output_voltage, channel, gain, DAC_CS, errmsg);
scale.power_down(); // put the ADC in sleep mode
delay(50);
scale.power_up();
//only run when data is available
if (Serial.available() > 0){
serial_DAC_set();
}
}
// just use this function to set the voltage on either channel A or B
//function to set state of DAC - input value between 0-4095
void DAC_set(unsigned int input, char DAC_sel, char Gain_sel, int CS_pin, String &errmsg)
{
//DAC_sel choose which DAC channel you want to write to A or B
//Gain_sel choose your gain: H=2xVref and L=1xVref
byte MSB,LSB;//most sig, least sig bytes and config info
//clear error messages
errmsg="";
Serial.flush(); // in case of garbage serial data, flush the buffer
delay(10);
//only run the rest of the code if binary is in range.
if (input<0 || input >4095)
errmsg += "input out of range. 0-4095.";
else
{
//convert decimal input to binary stored in two bytes
MSB = (input >> 8) & 0xFF; //most sig byte
LSB = input & 0xFF; //least sig byte
//apply config bits to the front of MSB
if (DAC_sel=='a' || DAC_sel=='A')
MSB &= 0x7F; //writing a 0 to bit 7.
else if (DAC_sel=='b' || DAC_sel=='B')
MSB |= 0x80; //writing a 1 to bit 7.
else
errmsg += "DAC selection out of range. input A or B.";
if (Gain_sel=='l' || Gain_sel=='L')
MSB |= 0x20;
else if (Gain_sel=='h' || Gain_sel=='H')
MSB &= 0x1F;
else
errmsg += "Gain selection out of range. input H or L.";
//get out of shutdown mode to active state
MSB |= 0x10;
Serial.println("binary input to DAC: ");
Serial.print(MSB,BIN);
Serial.print(" ");
Serial.println(LSB,BIN);
//now write to DAC
// take the CS pin low to select the chip:
digitalWrite(CS_pin,LOW);
delay(10);
// send in the address and value via SPI:
SPI.transfer(MSB);
SPI.transfer(LSB);
delay(10);
// take the CS pin high to de-select the chip:
digitalWrite(CS_pin,HIGH);
}
}
//function to read user command from the serial command window and set the DAC output
void serial_DAC_set(void)
{
unsigned int n; //number 2 bytes long unsigned
char channel, gain;
String errmsg; //errors returned from DAC_set
// read the incoming string:
inputString = Serial.readString();
// say what you got:
Serial.print("I received: ");
Serial.println(inputString);
//extract channel address
channel = inputString.charAt(0);
//and gain
gain = inputString.charAt(1);
//convert string to an int - binary voltage
n = inputString.substring(2).toInt();
//set DAC state
DAC_set(n, channel, gain, DAC_CS, errmsg);
//print errors if there are any
Serial.print("n: ");
Serial.println(n);
Serial.print("channel: ");
Serial.println(channel);
Serial.print("gain: ");
Serial.println(gain);
Serial.print("DAC_CS: ");
Serial.println(DAC_CS);
Serial.print("errmsg: ");
Serial.println(errmsg);
//clear input string ready for the next command
inputString = "";
}