For my dayjob, I work with people who have physical and mental disabilities. Some of these people are also analphabetic and that can sometimes cause problems for the work they have to do. One of the jobs they do is repackaging sweets. The problem is that repackaging sweets is always based on weight and that they are not able to read scales. So I decided to do something about that.

Since teaching them to read and write would cost me to much time, I decided to build a device that does that for them. My task was made a lot easier by the fact that we use industrial scales that have a serial output to connect it to a printer. It meant that I didn't have to open the scales themselves to hack a device into it. I could just add a serial device to it.

I came up with this device. It has 3 LEDs to display the weight. Green means that the weight is ok, red means that it is to low and yellow means that it is to heigh. A foreman can set the device up via the LCD and the rotary encoder.

It is also possible to set separate percentages that the weight can be above or below the required weight. I added that feature because sweets have a certain weight and it isn´t always possible to get the required weight exactly.

An extra feature is that the value off the desired weight and of both percentages are stored in the EEPROM of the device, so that it sets itself to the last used values on powerup.

Step 1: What Do You Need to Build the Circuit?

  • A 16 x  2 LCD
  • An Atmega8
  • A 74LS04D
  • 3 x 150Ohm resistors
  • 6 x 10K resistors
  • A 10K trim potentiometer
  • 2 x 100nF capacitors
  • A red LED
  • A green LED
  • A yellow LED
  • A rotary encoder with a pushbutton function.
  • A male 9-pin serial connector

Step 2: The Circuit:

The circuit for this device is easy to build. There are only a few components and although I opted for smd parts, everything can be build with regular thru-hole parts.

The brain of the circuit is an Atmega8. I did not add an extra crystal as I made it run on its 8Mhz internal oscillator.

The rotary encoder is connected to the interrupt pins. Channel A is connected to INT0 (PD2) and the pushbutton to INT1 (PD3). Channel B is connected to PD4. Three 10K pullup resistors connect these lines to 5V to keep them high.

PD5 to PD7 are connected to the LEDs via three 150Ohm resistors.

The serial input is connected to PD0 (RXD) but that is a problem, because the signal is 9V and it's also inverted. You can buy dedicated IC's to address this problem, like the MAX232 ic. But for this project it was easier and cheaper to use a 74LS04D inverter in combitation with a 10K resistor. The 10K resistor brings the voltage level of the signal within acceptable limits and the 74LS04D inverts the signal so that the Atmega8 can understand it.

The LCD is connected to port C of the microcontroller. A trimpot is added to adjust the contrast of the LCD.

I did etch the first board myself but it had some issues so I decided to order a pcb at iteadstudio. They turned out to be really great boards.

Step 3: The Code: the Serial Communication.

The code consist of one main loop and three interrupt routines. Two of those interrupts are triggered external interrupts INT0 and INT1. These are connected to the rotary encode. INT0 is triggered by rotating it, INT1 by pressing it. The third interrupt is triggered by receiving a specific byte (in this case '10') via the serial input.

How does the communication with the scales and the dataconversion happen?

The scales that I use is a KERN PCB 6000-0. It has a mode where it continuously sends 18 byte datablocks containing the weight through the serial port. The atmega8 buffers the 18 incoming bytes. The last transmitted byte is always a Line Feed (ascii 10). As soon as the serial receives the Line Feed (ascii 10), an interrupt is triggered and the code switches to the interrupt routine. This routine starts by clearing the variables needed for the dataconversion. It starts to read the serial buffer. Although there are 18 bytes in every datatransfer, only byte 9 to 12 contain the actual weight. Those bytes are stored for conversion. All other bytes are dumped into a temporary variable to flush the buffer.
The stored bytes contain the 4 digits of the weight as ascii characters. This means that we have to convert them to the correct decimal values before we can do any calculations with them. Luckily this can be done very easily by substracting 48 from the incoming values as every ascii value is 48 higher than the decimal value of the same character.
Then the value of the first character is multiplied by 1000, the second by 100 and the third by 10. Those three results are then added together and the fourth value is added. This final result is an integer containing the weight measured by the scales.

Once this weight is known, it is compared with the desired weight in the main loop and the LEDs are set.

Step 4: The Code: Reading the Rotary Encoder.

I often read on forums that people have problems with rotary encoders or that they have to use fancy libraries if they want  to use them. BUT: rotary encoders are in fact really easy to use and they take only a very few lines of code. No matter wheter you code for arduino or in C or in bascom or any other language.

If you look closely at the diagram, you will see that when turning right, B is always low on the rising edges of A. On turning left B wil always be high on the rising edges of A.
So if we attach A to INT0 and set it to trigger an interrupt on the rising edge, then al we need to do in the interrupt routine is checking whether B is 0 or 1 to know the direction. Depending on the direction, a counter is is increased or decreased and this counter can then be used in the main loop to set the values of a range of variables. It is really as simple as that!

in Bascom code:

dim count as integer

A Alias Pind.2 '(INT0)
B Alias Pind.4

config A as input
config B as input

Set B
Reset count

Config Int0 = rising
On Int0 Getencoder

Enable Interrupts
Enable Int0

'main loop here


   waitus 250
   If A = 1 Then
   If B = 0 Then Incr Count Else Decr Count
   End If

The pushbutton fuction of the rotary encoder is simply an interrupt that is triggered by connecting the in to INT1. In the matching routine, all kindst of stuff can do depending on what you would like to happen. In this case it is used to loop through the menu,to accept the values and to write them to the EEPROM of the atmega8.

Step 5: The Code: Full Code

Here I add  the complete code as a reference. It was written in BascomAVR but it should be fairly easy to translate it to C or for arduino. I did comment most of the steps but please contact me if anything isn't clear to you.

$regfile = "m8def.dat" 'Register file for the atmega8
$crystal = 8000000 '8MHz clockspeed
$baud = 1200 'baudrate = 1200

'declaration of the variables
dim count as integer
dim syscount as byte
dim i as byte
dim temp as integer
Dim weight as Integer
Dim comm(4) as byte
dim calc(3) as integer
Dim weightwanted as integer
dim weightwantedram as ERAM integer 'stored in EEPROM
dim percentplus as byte
dim percentplusram as ERAM byte 'stored in EEPROM
dim percentmin as byte
dim percentminram as ERAM byte 'stored in EEPROM
dim topvalue as integer
dim lowvalue as integer

'Aliassing pins
A Alias Pind.2
B Alias Pind.4
Red Alias PORTD.5
Green Alias PORTD.6
Yellow Alias PORTD.7

'Direction of the pins
Config A = Input
Config B = Input
Config portd.3 = input
Config portd.5 = Output
Config PORTD.6 = Output
Config PORTD.7 = Output

config serialin = buffered, size = 18, bytematch = 10 '18 bytes buffered and triggers interrupt on receiving a LF

'setup of the LCD
Config Lcdpin = Pin , Db4 = Portc.3 , Db5 = Portc.2 , Db6 = Portc.1 , Db7 = Portc.0 , E = Portc.4 , Rs = Portc.5
Config Lcd = 16x2
cursor off

'setting certain pins to 1
Set B
set pind.2
set pind.3

'configuration of the external interrupts
Config Int0 = rising
On Int0 Getencoder

Config int1 = Falling
on int1 menu

Enable Interrupts
Enable Int0
Enable int1

cls 'clear screen

Reset count
Reset weight
Reset syscount

'main loop

select case syscount 'depending on syscount an screen will be selected for the lcd

waitms 25

cls 'clear screen

case 0: 'main screen
weightwanted = weightwantedram 'reads these 3 variables in the EEPROM and stores them in global variables
percentplus = percentplusram
percentmin = percentminram
locate 1,1 'writes the desired data to the lcd
LCD weightwanted; "Gr"
locate 1, 8
LCD "-";percentmin;"%"
locate 1, 13
LCD "+";percentplus;"%"
locate 2,1
lcd weight; "Gr"
reset Red 'turns all the LEDs off
reset Green
reset Yellow
topvalue = weightwanted * percentplus 'calculation of all needed values.
topvalue = topvalue / 100
topvalue = weightwanted + topvalue
lowvalue = weightwanted * percentmin
lowvalue = lowvalue / 100
lowvalue = weightwanted - lowvalue
if weight <= topvalue and weight >= lowvalue then set Green 'sets the correct status LED
if weight > topvalue then set Yellow
if weight < lowvalue then set Red

case 1: 'setup weight screen

locate 1,1
lcd "Setup:"
locate 1,8
lcd "Gewicht"
weightwanted = count 'reads the rotary encoder and sets the desired weight
locate 2,8
lcd weightwanted

case 2: 'setup overweight screen

locate 1,1
lcd "Setup:"
locate 1,8
lcd "+%"
percentplus = count 'reads the rotary encoder and sets the overweight
locate 2,8
lcd percentplus

case 3: 'setup underweight screen
locate 1,1
lcd "Setup:"
locate 1,8
lcd "-%"
percentmin = count 'reads the rotary encoder and sets the underweight
locate 2,8
lcd percentmin

end select

loop ' end of the main loop

Getencoder: 'triggered by INT0. Reads the rotary motion of the rotary encoder

waitus 250
If A = 1 Then
If B = 0 Then Incr Count Else Decr Count
End If
GiFr=64 'debounce

menu: 'triggered by INT1. Reads the push button of the rotary encoder

incr syscount 'increases syscount. This variable is used to decide what screen is shown
if syscount > 3 then syscount = 0
select case syscount
case 0: percentminram = percentmin 'writes to EEPROM
case 1: count = weightwanted
case 2: weightwantedram = weightwanted 'writes to EEPROM
count = percentplus
case 3: percentplusram = percentplus 'writes to EEPROM
count = percentmin
end select

waitms 150
GiFr = 128 'debounce


serial0charmatch: 'is triggered by the serial interrupt
weight = 0 'resets variables
for i = 1 to 3
calc(i) = 0

for i = 1 to 18 'reads the needed bytes into variables and dumps the ohter bytes to empty the buffer
if i = 9 then comm(1) = inkey()
if i = 10 then comm(2) = inkey()
if i = 11 then comm(3) = inkey()
if i = 12 then comm(4) = inkey()
if i <> 9 and i <> 10 and i <>11 and i <> 12 then temp = inkey()

for i = 1 to 4 'translate ascii to dec
if comm(i) > 47 then
comm(i) = comm(i)-48
comm(i) = 0
end if

if comm(1) > 0 then calc(1) = comm(1)*1000 'makes 1 number out of 4 caracters
if comm(2) > 0 then calc(2) = comm(2)*100
if comm(3) > 0 then calc(3) = comm(3)*10
weight = calc(1)+ calc(2)
weight = weight + calc(3)
weight = weight + comm(4)


Step 6: The Case

To build the case, I choose 5mm thick frosted acrylate. The panels were lasered at IMAL (Fablab in Brussels). One thing you should keep in mind when lasering frosted acrylate is that the frosted layer shrinks a bit on the top side when the laser hits the material. So you should always keep your 'good side'  facing down.

Everything was glued together with Acrifix 1S 0117, a special glue for acrylate. One problem of this glue is that it is more fluid than water. It isn't easy to work with it without dripping glue all over your project.  So here is a great tip for you all: you can thicking this glue! Just fill a glass jar with the glue and add some leftover pieces of clear acrylate. They will completely dissolve in the glue after a few hours (you might need to stir it a bit) and make the glue a bit thicker.
Great Project - nicely designed &amp; built. <br> <br>I have also found ITeadstudio to be great and fantastic value. <br> <br>One thing that might interest: <br> <br>You can read the encoder with equally simple code and circuit but twice the resolution if you interrupt on a change in A rather than just A rising. You will see from the diagram that you posted that when turning Right, A always changes to be different from B (A is leading). Going Left, A always changes to be the same as B (A is following). <br> <br>Therefore, if you interrupt on change in A then you can decrement the counter if A=B and increment if A=/=B. <br> <br>Ugi
This was exactly what I was thinking when I read his code! I did that with an encoder taken from an old mouse scroll wheel just a little bit ago.
You are right indeed. But when you do an interrupt on change, them it does 2 steps per 'click' with my encoders.
I really have no need for this but read it because I am a nerd. I really wanted to comment on you using your skills to help others. The world would be a better place if more people followed your example and thought about lifting those that are disadvantaged. Kudos to you!
Excellent work you are a great person you might want to look into selling this because i bet their is something already like this out their but costs silly money just because its a disability aid.
Great job. Thanks for using your talents to help others be successful. Thanks for posting.
Very good idea!
Well done!
Such a wonderful project! Great documentation, too :D

About This Instructable




Bio: I'm mainly interested in music, food and electronics but I like to read and learn about a lot more than that.
More by janw:Pickled Chillies Extra hot sriracha sauce (Mme Jeanette) Build a Nest Box for Bumblebees 
Add instructable to: