Author Topic: Moteino Kitchen Scale  (Read 43667 times)

kolumkilli

  • NewMember
  • *
  • Posts: 24
Moteino Kitchen Scale
« on: October 31, 2013, 10:08:34 PM »
I am building a network of Moteinos to manage various measurement, feedback, and actuation tasks.
Currently I have a lot of Atmega-based nodes, many wired, others on WiFi, and yet others using RFM12B.

Moteino R4 with RFM69HW is so excellent I have decided to convert everything over to them as a platform, and additionally use a handful of R4-USBs to manage a distributed, redundant gateway setup for getting measurements and commands over to my TCP network.

One setup I have is a self-built water-distiller.  It has a variety of sensors and actuators.  There is a distilling flask with an electric burner, a steam condensing column (water jacketed heat exchanger), a water reservoir with a pump, and a copper-coil heat exchanger with fan (air flow cools the water in the coil, which is the water that extracts heat from the steam in the condensing column). 

There are a lot of constraints and I am always optimizing processes, but in order to do that one needs numbers!  I have several nodes with Dallas temperature sensors, at key points in and around the system.  I have nodes with water flow rate measurement.  I have nodes with air flow measurement.  And I have two kitchen scales, one of which I have just replaced with my brand new Moteino conversion!  For actuators I have relays controlling power to the burner, and to the water circulation pump.  I used to have a thing that varied the fan speed in the heat exchanger but I got rid of that because every optimization I ran had me just maxing that out, all the time.

The burner and distilling flask are on one scale, and there is a second scale to weigh the condensate, caught in a beaker.   The beaker holds 500mL.  The pump, located in the reservoir, can only take water temperatures up to about 30C or so (meaning, that is the rating - I don't push it).  With a little attention, it is possible to dump the beaker and refill the flask, and obtain a more or less continuous production of distilled water.  The heat exchanger (as is currently) cannot keep up with the heat extraction, so the reservoir water does heat up over time, and when it hits a set point, it shuts off the burner, but continues to circulate so that the cooling water does cool down again, to another set point, at which time the burner turns back on.  When the burner is off and boiling ceases, it is possible to add more water to the boiling flask.  The original problem, before I put the scales in, was that I had no signal when the beaker was full.  I had to be extra vigilant.  So I put a kitchen scale in under the beaker, so that I would know when it got to 500 mL (or a mass of 500g, really).  Then that scale, a node, could also actuate the burner off, until I replaced the beaker with an empty one.  So the system waits for me, if I don't have a chance to empty that beaker.

Then I decided to put the boiling flask on another scale to measure the mass loss through boiling, to see how (in)efficient the process was.  Turns out, it was much less efficient than I had thought before I had the measurement!  I found leaks in the fill tube, out of which steam was escaping, and also I found that in the condensing column, in addition to the liquid condensate, steam was escaping directly - uncondensed.  These findings allowed me to make adjustments to the water jacket temperature and flow rate, and increase production rate.

So that is the setup.  Today I finished the first Moteino kitchen scale, the one that goes under the beaker.  Moteino made it easy.  In this scale I decided to hack the LCD readout, instead of interfacing with the Wheatstone bridge directly with an instrumentation amp, which I had done with one of the previous scales.  Moteino's access to the extra ADCs were important, because this was a 16 pin LCD.  I was able to move pure analog measurements (like battery voltage) to those ADCs, and use ADC0 thru 5 for the LCD pins.  I was able to reduce the number of pins I needed to 11, and I was able to detect the waveform by repeatedly checking the bits in PINC and PIND, for about 40 milliseconds, and then analyzing the binary data for patterns.

One hitch was that this particular scale went to sleep after about 45 seconds of no change.  I could actuate the on button to turn it back on, but that wouldn't help since that would re-zero the scale, with whatever it had on it.  I could store the previous reading and keep adding, but that wouldn't help me if I was just standing in front of the scale and wanted to see the measurement.  But I found that by changing units, it would reset the inactivity timer in the scale.  So, I could just periodically change to ounces and then back to grams in less time than the human eye could detect, and the scale would stay on for as long as I cared to have it on.  (And it is not mutually exclusive with turning it back on, should it somehow turn off).

The switch debounce timing wasn't so consistent in the scale's onboard computer, so sometimes it would not register a "click", so I had to always sample the LCD to see where it was and then keep clicking until it was in the state I wanted it to be.

I can transmit the reading periodically, and I also have it so that you can request the current reading at any time over RF, including if the scale computer is off.  Additionally, I have the scale transmitting its battery voltage periodically so that I can monitor the "health" of the node.  (I do this will all battery-based nodes - I need a failsafe if a critical-decision node like the beaker fill level is not operating, the process must shut down if it can't tell how much there is).

So, one node down (not including the gateway), many more to go, but I think the rest of them will be much easier.  I will post some pictures later if there is interest.

I am very excited to be doing this all wirelessly (instead of some nodes here and there), and with the wireless updating of sketches this is really going to make it easy to optimize the system, just using software from wherever I happen to be sitting.  No more stringing wires around, no more leads pulling half full beakers off the table.   Now all I need is a huge magnetic coil to continuously charge the batteries by induction, wirelessly.

I am also considering a wearable Moteino with a small OLED display, to just tell me various critical things, how much water, what is the water temp, etc.  Right now I have a wired node that just makes audible tones whenever it is doing something I ought to pay attention to, then I would go over and read the scales, check the temps, etc.   Would be nice to have a little screen with just the right parameters, that I could glance at wherever I am.   I thought about an Android app, but seems like a Moteino monitor would be much much easier, and then it all stays on the RF network, all on one platform.  Low power to conserve battery life.  Looking forward to some RF range testing too!  (And it's not mutually exclusive with a future Android app, if I ever get to that.)

Thank you Felix, for providing just the right thing to make this all possible!
« Last Edit: November 01, 2013, 02:34:37 PM by kolumkilli »

jbeale

  • Jr. Member
  • **
  • Posts: 62
Re: Moteino Kitchen Scale
« Reply #1 on: November 01, 2013, 12:14:30 AM »
Impressive first post!  Sounds like you are enjoying the Moteino system, as I am. Sounds like quite a complex system you have... I guess I'm lucky in that I can just pick up distilled water by the gallon pretty cheaply at the grocery store.

kolumkilli

  • NewMember
  • *
  • Posts: 24
Re: Moteino Kitchen Scale
« Reply #2 on: November 01, 2013, 07:48:39 AM »
Thanks very much!  Yes, the Moteino system is just excellent, I am very pleased so far.

Yes, grocery distilled water... I looked into that as a possibility but unfortunately, tests I ran on it were not consistent.  A lot of times the water has unacceptable electrical conductivity.  It probably means that some large plant making the stuff has metal in the system, or other contaminants.  In mine, small as it is, the water only touches borosilicate glass (well, and air), so I can control its properties very well.   And if you put grocery distilled water into the distiller, and boil it away, you will see what remains when the water is gone - it varies, but it is not nothing.  I don't know what people use it for, other than maybe car batteries, but for the tightly controlled chemical processes I work on, it would add one uncontrolled variable to the mix.  It is definitely a way better starting point than city water though, I'll attest to that!

That said, the distilled water is a needed by-product of my main activity.  I work on data-driven process optimization, so all the practice I can get with the distiller results not only in good-quality distilled water, but also knowledge about how the data of the system behave.  And it's a great demo too, because you can pretty much show the basic concept with a simple interacting closed (distillation) and open loop (heat extraction) system.

The Moteino just ups the coolness factor.  The coefficient of coolness is being severely optimized here, soon I will have Moteino everywhere!

Even the LCD hack was fun for me, I learned a lot.  As I mentioned, I had to optimize to get from 16 pins down to 11.  A small optimization task, but fun.  This post was supposed to be less about  the distillation and more about the kitchen scale hack - but I had to give the background...  With the LCD, I found that the first four pins were control channels, and the remaining 12 were the data channels.  Monitoring the waveforms, I saw that the four control channels were always identical, but shifted in phase by 90 degrees, one after the other.  So I only needed to monitor one channel - three pins freed up!  Then I found that some of the segments were redundant.  Such as the colon that the display had between pounds and ounces only came on when the "lbs" unit segment was lit - so no need to monitor that one.  I got rid of two more pins that way.

The control waveforms, when sampled digitally repeatedly, every 500us or so, produced this:

00000111000111000111111000000011100011100011111100000011

This sample covers more than a full cycle, nearly two, guaranteeing that the signal repeat can be captured.  You can see the long sequence of 1s - those indicate a high pulse just before the waveform drops the other way, to the trough of the control signal.  The zeros following those sequences are the low pulse.  So by this method I could find the approximate middle of the pulse, and create an index through the data, and with the second sequence, find a second index, which allowed me to know the length of the full cycle.  Divide the difference by 4, and I have the indices of the three other control channels.  The data from the entire array (with non-measured pins removed) are like this:

                         Index1                   Index2
                           |                        |
                           V                        V
0: 0000011100011100011111100000001110001110001111110 0000011
4: 0011100011100011100011100001110001110001110001110 0011100
7: 0011100011100011100011100001110001110001110001110 0011100
8: 0011100011100011100011100001110001110001110001110 0011100
9: 0011100011100011100011100001110001110001110001110 0011100
A: 0011100011100011100011100001110001110001110001110 0011100
B: 0000000000000000000000000000000000000000000000000 0000000
C: 0011100011100011100011100001110001110001110001110 0011100
D: 0000011100011100011111100000001110001110001111110 0000011
E: 1100011111100011100000011110001111110001110000001 1100011
F: 0011100000011111100011100001110000001111110001110 0011100


By just going down the line, if there is a 1, you know that segment is on, and if 0 it is off.
This is very precise, I have found I only require one set of samples like this to read the LCD.  There is no need for repeated samples and statistical averaging, like I have seen in other implementations.  Of course, this may vary by LCD...  this one had a waveform and voltages that worked very well with this kind of sampling.  I'll let you know when I do the other scale, which is of a different make!
« Last Edit: November 01, 2013, 08:12:13 AM by kolumkilli »

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Moteino Kitchen Scale
« Reply #3 on: November 01, 2013, 08:21:36 AM »
Wow, a lot of work just to read an LCD!
I always wondered if I could read/log my digital bathroom scale, well now I got my answer :)
Cool stuff in here kolumkilli, quite impressive setup, thanks for sharing!

kolumkilli

  • NewMember
  • *
  • Posts: 24
Re: Moteino Kitchen Scale
« Reply #4 on: November 01, 2013, 10:18:08 AM »
Thanks Felix!

I found it really helps if the original scale circuit board has test pads on the LCD connector, to which you can solder your leads.  If not, it's a real pain, and on my other scale I ended up just ripping that all out and wiring up the sensors directly.  But, that was much more difficult.  There is a lot more tuning to get your measurements to span the full scale of the sensors, etc.  There is a lot more trial and error. 

The LCD waveform is essentially digital,  so there is no guesswork or approximation.  The bit is either on or off.  It's not about how many microvolts (!) equals one gram, and the sensitive tolerances on that.  When you just read the LCD, all that tuning has already been done by the original circuit design and on-board computer.

So, if you are going to hack your scale, I highly recommend this method over the Wheatstone bridge method, and I also recommend: pick the right scale!  (Unfortunately, you have to buy the scale before you can take it apart to see if there are LCD test pads... I can't advise on bathroom scales, but I can advise on about 4 or 5 different makes of kitchen and postal scales, if you need.)


jbeale

  • Jr. Member
  • **
  • Posts: 62
Re: Moteino Kitchen Scale
« Reply #5 on: November 01, 2013, 01:25:45 PM »
Neat stuff... I was using distilled water just to make my own bubble solution for the kids, not trying to do real chemistry. I guess real DI water is a little more expensive... like $400 per liter! If that was a wine, it would make the top-50 list.

This is not Moteino-related, but if you want to read a LCD display without soldering wires or having any electrical connection to the device, and you have a camera (eg. Raspberry Pi with webcam or Pi-cam) you can do optical character recognition on the display.  The program is called 'ssocr' (seven-segment OCR).  You need a clean image, and have the thresholds set right, but it does work. (Also- it seems to only read digits, not the decimal point)  The OCR algorithm uses small images (relatively few pixels), so if the geometry allows, you could read quite a few different displays at once with the same camera.  You get misreads if the frame is captured during the time the display is changing value.
See also: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=37&t=57727

Apart from not needing a camera, your electronic readout setup has the advantage of being synchronized to the display so there's no update sync problem.
« Last Edit: November 01, 2013, 01:46:25 PM by jbeale »

kolumkilli

  • NewMember
  • *
  • Posts: 24
Re: Moteino Kitchen Scale
« Reply #6 on: November 01, 2013, 02:33:54 PM »
Yeah, DI water is not cheap.  Even if you make it yourself, there is the constant refreshing of the resin canisters etc.  I find for most things I can just use tightly controlled distilled water... so that's what that is all about!

Very cool on the OCR, I will have to check that out!

Here are some photos of this kitchen scale, before and after.  Sorry it's a little blurry, I only had my phone handy at the time, to take the pics.  Thanks to Felix for posting these up so I could link them back in the forum.

This is a picture showing all the segments lit, I took this on scale initialization:




Here is the underside of the scale, showing the four sensors and the battery compartment:



With the back removed, you can see inside, there is not too much to it.
A single control board, with a direct connection to the LCD, and wires to the four sensors, each of which is a half-bridge.  The four, wired together, make a standard Wheatstone bridge.



In this picture, I flipped up the control board, and you can see all the round test pads. Very convenient for soldering!
The ninth pin from the right did not have one, so for that one I had to take an small blade and scrape away until the trace below was uncovered a bit, then I could solder to that spot.  Had to stay clear of the contact to the LCD connector, which is the little grey bar just below the three screw holes. 



All the juicy microcontroller goodness is under that black epoxy circle... so nothing to try to mess with, really.  This was a very cheap scale, around $10.  There seems to be a spot for an EEPROM - that must be for the $12 deluxe model....

Here is a picture with it all assembled.  The wires from those pads just run to the inputs on the Moteino.
I soldered the FTDI contacts to a cable and put a 4 pin JST style connector on the end, so that I could just plug in a cable when I want to re-flash or interface to it by USB directly.  (I don't pass USB power.)  Also added was a LiPo battery and charger from Sparkfun, with the micro-USB connector also sticking into the battery compartment.  Maybe it is more battery than I needed, but it lasts a long time!  Also note the black wire from the Moteino, snaking around the edge of the scale.  That is the half-wave antenna.  Also note, this is an R3 Moteino with RFM69W, not an R4, but the next one will have R4.



With the back on, I can still use the battery compartment as an opening to have access to the FTDI and the micro-USB for battery charging:


So, that's about it... 

Oh, one more thing, if you look back at that first image, you will see the segments correspond to this map:


The label in each segment consists of two digits.  The first digit is which control channel is low when this segment is lit.  The second digit is the hex digit of the pin number that is high when this segment is lit.  So "0F" is when the waveform on pin 0 is low, and waveform on pin 15 (= F) is high, thereby lighting the segment.  The control waveforms are pins 0 through 3.  pins 4 through 15, or F, are the "bits" that control the segments.  "0F" corresponds to the "g" in the lower right hand corner of the display.

« Last Edit: November 01, 2013, 03:01:49 PM by kolumkilli »

kolumkilli

  • NewMember
  • *
  • Posts: 24
Re: Moteino Kitchen Scale
« Reply #7 on: November 02, 2013, 07:44:14 PM »
This is a picture of a scale I had rigged up before I found Moteino.



The original control board was removed entirely - it had no real estate to make solder connections to the LCD, and everything on it was a real tight fit.  So I elected to just interface to the four half-bridge sensors directly.

Just one note, this was an "ultra-thin" scale, so the height available to me was about a quarter inch.  Since that was the case, I had no real choice except to take the ICs, flatten out their pins, and solder directly to them.  This was just a prototype, and it did the job - and is still doing a good job.  I don't have a "before" picture, but the entire thing was just empty.  Same as the other scale, it was four sensors in the corners, and the tiny main control board right beneath the LCD - that was it apart from the two 2032 batteries that powered it.

The added component inventory is as follows:

1 )  The little square blue component with the three leads is just a precision potentiometer.  It is there to adjust the gain on the instrumentation amplifier.
2 )  The instrumentation amplifier, necessary to turn the microvolt signal from the Wheatstone bridge sensors to something more in the range of what the ATMega328P can read. It is the little 8 pin IC near the potentiometer.
3 )  The ATMega328P, in the large 28-pin PDIP packaging. (Direct soldering to the tiny pins of the TQFP package is maddening, and I do not recommend it to anyone.)  Plus an oscillator crystal and various capacitors associated with the microcontroller.
4 )  An RN-171 WiFi module, the darker blue component in the center of the scale, meant for surface mounting, with the resistor sticking out of it (that's the antenna lead - they wanted the circuit trace to the antenna to be 50 Ohms, but since I wasn't mounting this to a circuit board, I just put a resistor on there.  Can't complain, it gets good reception).
5 )  A voltage regulator, and some decoupling capacitors.
6 )  A 128X64 OLED screen, obscured in this picture by e-tape, but it is the blue component that is under that large white piece of foam.  That foam is to squeeze the screen up against the glass surface of the scale when the back is put on.  It works.
7 )  A rechargeable battery (held in by spring brackets made from blobby, flexible epoxy, not pretty but it works pretty well.)
8 )  The same type of Sparkfun LiPo charging board used in the new scale in the original post.
9 )  A CP2102 USB to logic level serial interface (tiny red board next to the Sparkfun charger).  It had its own USB plug which I removed, and soldered the data connections to the Sparkfun charger micro USB, so I could just interface and charge right thru the single connector.  Soldering to that micro-USB connector was not fun at all.  I epoxied it all together once the electrical connections were made, as it was mechanically fragile.
10 )  various voltage divider resistors.

So, this thing works.  It has a very simple webserver that can take a few commands and serve up the scale reading.  The sole advantage is that I can just pull it up on a browser from anywhere on my TCP network.

But now compare with the Moteino scale of the original post.  By interfacing with the LCD, we don't need components 1 and 2, as they are built into the original control board, and 6 goes away because I retained the original LCD.  I keep the battery and charger, and all the rest of the components (substituting the RFM69W for the WiFi, the point is it is still digital data over wireless connection) are replaced entirely by Moteino.  To be fair, component 9 is additional compared to the scale in the original post, but could be substituted if I had simply used Moteino R4-USB.  (But I decided, that with wireless update, I really don't need this component at all.  So in that sense, since Moteino has wireless data interface thru RFM69W, and can flash the chip wirelessly as well, it actually DOES replace the CP2102 interface, functionally, without additional hardware.)

All the aggravating wire routing between components and soldering between them all goes away - it is all on board the Moteino.  No more jamming in numerous components.  No more epoxy.  And, as I can have my new Moteino scale talk to my new Moteino R4-USB gateway, I can still get the info from a browser - now hosted on the much more suitable gateway PC.  Better really, because I'm not limited by the tiny MCU on the kind of web delivery I can code, but also, no need to manage all the IP addresses of every single node.  The PC-based webserver can retrieve data from any number of RF nodes in range, and arrange the data conveniently for web service, mostly for human viewing convenience.

Let's talk power.  All those chips and the WiFi module are hungry!  Even with the relatively large battery, I can only keep this thing on for a few days continuously.  I might be able to stretch it to a couple of weeks with more low power programming, throttling the duty cycle and so forth, but really it's that WiFi module's availability that governs the equation.  With a comparable battery I think the Moteino could last a year or more before I have to charge it.  And the data availability is there since the web service is moved to PCs with continuous power. With a scale, you don't need to sample the data more than a few times a second as a maximum rate - for my usage I really only need a measurement once per minute or so.  So with Moteino, the actual data sampling can now occur at a slow rate, and the data availability can be brokered by the always-on PC webserver.  But even if a direct query is necessary, the radio can sleep and wake up with a very low duty cycle and still maintain fast direct availabilty, so it is totally not dependent on the gateway at all - it is a fully independent and addressable node, for direct node-to-node communications.  Looking forward to the once-a-year battery recharge interval.

Did I mention the cost?  Easy to do the math here - the RN-171 WiFi module alone costs more than the Moteino with radio.

Take a look at the two side-by-side.  Both functional, but one expensive, complex, inefficient.  The other, simple, elegant, efficient, and inexpensive.  Thank you again, Felix, for making it possible.









jbeale

  • Jr. Member
  • **
  • Posts: 62
Re: Moteino Kitchen Scale
« Reply #8 on: November 02, 2013, 10:10:40 PM »
Impressively detailed and well illustrated writeup- thanks for that! Amazed you had the patience for that first pre-Moteino scale project.

One question if I might, you wrote: "Also note the black wire from the Moteino, snaking around the edge of the scale.  That is the half-wave antenna."

Are you actually using an end-fed half-wave antenna with no matching network? A half wave antenna (dipole) is normally center-fed for something like 73 ohm impedance. In theory, an end-fed half-wave wire should have a very high impedance, maybe several kilohms, and if you connect directly to a RFM69 module tuned for 50 ohms, you would not expect to get a good match to get power into or out of it.  Maybe it is really a 3/4 wave antenna?  There is also something called a "J-pole" which you can think of as a 1/4 wave tuning stub plus 1/2 wave end fed monopole.
« Last Edit: November 02, 2013, 10:23:18 PM by jbeale »

kolumkilli

  • NewMember
  • *
  • Posts: 24
Re: Moteino Kitchen Scale
« Reply #9 on: November 03, 2013, 07:39:24 AM »
Thanks!  Yes, the previous scale was definitely doing it the hard way!  With that experience behind me, I would recommend Moteino + monitor the LCD method over having to do absolutely everything with the Wheatstone bridge method.  I think it ended up being something like twenty times faster to complete the project.  Maybe faster - far far fewer headaches with Moteino.

By "half-wave antenna" I only meant a half-wavelength monopole wire antenna, as per the legend of the Moteino pinout diagram:



So, it's just a wire soldered to the ANT terminal.  But, it works quite well.  All my nodes are within 200 feet of each other, though.  There are a few walls in between, but I have not found any issue whatsoever.   Short answer is, a simple wire of the indicated length, has always just worked for my particular situation, so I never sought to optimize this further.

But, I've been meaning to do some range testing, I have some future applications in mind, and am interested in signal penetration through objects more than maximum line of sight distance through air.  I would humbly accept any advice you have on optimizing this part of my gear, for sure!  I'm just starting out my investigations with the RFM69W radio, most of my experience in 433MHz has been very short range, with RFM12B and with super cheap FSK and ASK modules commonly found in RF remotes.

The only optimization I have done so far is on a gross scale - I did find that the 1/2 wave length wire worked better than the 1/4 wave (as one might expect).  I've tried tight coils vs. snaking a mostly straight wire around in a casing, finding that the straight wire at the largest dimension allowed by a case (meaning, allowing for the largest possible minimum osculating circle in the wire path) worked better, and seemed to be less sensitive to polarization (when it ends up being not completely straight, which is always).  This aspect is important for the nodes I have that don't have a consistent directional orientation (unlike the scale, where down is always down, and level is always level).  Of course, nothing is ever ideal - for instance, that new scale has a stainless steel top surface which I am sure affects transmission, and the real estate for the antenna internally causes it to turn some corners rather sharply.   But, it works well, or well enough!  It's been completely reliable within 200 ft. of the other transceivers.  By that I mean, zero dropped bytes so far (and I always ack, never do unidirectional).

Also, I would not say I have done a statistically significant number of signal strength trials in various controlled conditions to state that the things I noticed, described above, are truly "better".  They've worked in the room I was in and seemed to me to be better, the few times I checked.  Far from my normal process when I am specifically quantifying something.

Your comments are most welcome - I'd love to try some optimization here and would be pleased to report back the results.

By the way, I want to draw a distinction between an optimized antenna and an ideal antenna.  I'm familiar with antenna basics but I can almost never effect a nice, straight, vertically polarized antenna with a ground plane.  The reality is always a dirtier job.  So by optimized I mean, works best under the various non-ideal conditions, such as being limited mechanically by the confines of a case.  Never as good as ideal, but then again, what is?  It could also be argued that ideal is not as good as optimized, since ideal is more ... difficult.  I'm going for the 85% solution over 99% of the time, as opposed to the 99% solution that can only be achieved 5% of the time (example numbers just for illustration).

So as not to get too off-topic in this post - you can see inside the scale case, see what it is about.  Would you route the antenna wire differently, change length, etc.?  Or use a different type of antenna?  I'd love to try out a suggestion, could use RSSI number with nodes at a defined distance apart as a metric, perhaps.  The scale will be faster to quantify since it does have a defined up and down axis.  I could plot signal strength in a defined circle around the scale's centroid, I suspect the edges where the stainless steel top curves down might cause a weaker signal along those radials - it would be fun to test that theory. 
« Last Edit: November 03, 2013, 09:11:29 AM by kolumkilli »

kolumkilli

  • NewMember
  • *
  • Posts: 24
Re: Moteino Kitchen Scale
« Reply #10 on: November 11, 2013, 03:17:49 PM »
A bit more about reading the LCD, as that is the crux of the kitchen scale conversion.  This post is going to be long (so long I had to break it into two posts), but I am writing it as a guide to make other people's lives a bit easier, and I thought the best way to do that would be by completeness.  In all my scouring of the internet for information about this subject, what I found lacking was the reasoning behind certain decisions or approaches.  I include my reasoning here so that you may agree or disagree, incorporate the techniques, or modify them to your purposes, with the knowledge behind it.  My application is my own, and I've done what works for me.  With this post I seek to give others the tools to move forward to their purposes, quickly.  Feel free to ignore anything that does not apply to your situation, certainly including matters of opinion, humor, and general commentary.  The technical statements should be solid, but anything erroneous I will be happy to correct!  Also, any general questions or suggestions on improving this post are most welcome.



The thing about it is, unless you are lucky, you probably need an oscilloscope to make sense of the signals on each LCD contact.  By that I mean, it is possible to just solder to the pads and take some readings and see if you get a nice pattern of ones and zeros, and if you have a bit of knowledge about what those patterns mean, you might be able to guess what is going on.  But more likely, you will have a waveform that needs a bit of deciphering, and that is made easy by using an oscilloscope.  If you don't have one, maybe this post will help you to take readings with just the Moteino itself, and increase the probability of guessing what is going on.

Here is a screen shot of the waveform of a control signal (one of four, they are all identical, but phase shifted in time by 90 degrees).  What we are after here is the deepest trough, the lowest point on the trace.  When this is "low", a high voltage on one of the other associated information channels, at the same time, will turn a segment on.  One could also try to detect the high peak on the control channel, and the low peak on the information channel, but that is more difficult, for reasons I will describe later.  I will leave the description of the LCD segment connection structure to the great oracle of the Internet.  There may be other names for what I call "control channel" and "information channel", but I use these terms because they make sense to me.



This post will focus on how to detect the voltages and make sense of them.  What we notice is that the lowest point on the control channels occurs directly after the highest point.  We also can tell easily from the oscilloscope that the voltages stay at each stable level of voltages for just about 2 milliseconds, a tiny bit more actually.  So, ideally, if you sample the voltage at any time within two milliseconds of the highest pulse ending, you should be sampling during the lowest pulse.  Now, two milliseconds is a small amount of time, and we have a lot of other things to do in that two milliseconds, so we want to optimize a few variables:

1) the number of data samples we have to take
2) the amount of time taking samples
3) the accuracy of the samples we receive.

There may be other variables to add to the mix, such as power usage, but I haven't fully optimized for that yet.  We'll get back to that later.

Here is a screen shot of the waveform of an information channel.  In this you can see the two millisecond medium-level pulses.  In this shot there are no high pulses, all segments activated by the combination of this channel with any of the four control channels, are currently not on.



In the following screenshot, we see a high pulse. 



This means a segment is on, the one that this info channel connects through to one of the control channels.  An oscilloscope makes it easy to find:



This was control channel 0, in combination with information channel 15 (F).  The point here is you need to know when a control channel is at its lowest, which information channels are at their highest, at that exact moment.  You essentially need to sample all of them at once.  The problem is, as it is so often, that we cannot stop time to get things done.  And the more things you need to get done, the more time you take doing them, the farther you are away from getting an instantaneous snapshot.  So we try to get the biggest bang (accuracy and precision) for the smallest buck (number of samples, time, power).

So we have to turn our attention to how long it takes to take a sample.  For reference, analogRead() takes about 100 microseconds.  At most you can get twenty of those samples in a two millisecond time span.  It takes too long.  One might be tempted to use analogRead() to work out the various levels within the waveform, but it is just on the border of workable for a single waveform, much less sixteen that you want to capture in a single "instant".

If we were to use digitalRead(), which takes somewhere around four microseconds to complete, then we could do about 500 of these within a single two millisecond pulse.  Plenty if you are sampling a waveform that has two millisecond pulses.  But you won't necessarily know that if you don't check it with an oscilloscope first.  But also, you have 16 pins to sample.  So at best that will take 64 microseconds.  You could get thirty sets of samples within that two millisecond pulse.  Still plenty.  But what if digitalRead() takes a non-ideal amount of time - there is variation.  To be safe we could say a digitalRead() could take as much as six microseconds - or phrased somewhat differently, it has a 95% probability (made up number for illustration) of being complete within 6 microseconds.  Now you can only do 333 samples within a single two millisecond pulse - or twenty sets of samples, but they are "safe" samples.  Still not bad.

But, how fast can you detect that the pulse has started?  The pulse flips from high to low in a finite, non-zero period of time, and you have some time between sets of samples, even if you are all out sampling with as little delay as possible between sets.  So you may not detect that you are in the pulse until one or two set of samples into the pulse.  You don't want to count on data at the end of the pulse either, where it may be transitioning out of its state.  So you are left with about 16 sets of samples you can potentially count on to be correct.  Well, that is pretty good, actually.  But, what if your pulse is only a nominal 1 ms?  That brings you down to maybe 6 sets of samples you can trust.  Still doable, but as you see, you whittle away at your margin of safety.

There is a better way - read the port input pins register directly, which takes about 150 nanoseconds.  Even better, reading the register directly gives you up to eight pins at a time, so for sixteen pins, if you choose them correctly, you could theoretically (only theoretically) capture all of them in two readings.  (The reality is that with the mix of pins you will be using for other things, you will likely need three readings, unless you cut the number of pins down.)  But still, three 150 nanosecond readings (but let's call it certain to 95% that we can get the reading within 200 nanoseconds) ... to get all 16 pins is 600 nanoseconds, as compared with 96 microseconds using digitalRead().  That's approximately (remember, we built in a certainty factor) 150 times faster.

Reading the registers directly is not difficult:

Code: [Select]
byte sample = PIND;

That gives you every digital value on Arduino pins 0 to 7, in one fell swoop.

The pin values are simply the bits in that single byte, and there are convenient definitions allowing for the extraction of these bit values.  Given the above assignment, the digital value of pin 3, for example, can be obtained by the expression:

Code: [Select]
sample & (1 << PIND3)

Pin 4, similarly, is given by

Code: [Select]
sample & (1 << PIND4)

The beauty of this is that all of the calculation to get these values can occur after you take the samples.  You do not have to waste time with extraction or analysis in between the sample taking, which is what digitalRead() essentially does.

If you are trying to read a single waveform, and get a stream of zeros and ones, just locate which pin register the single pin is on (PINB, PINC, or PIND) and simply grab the value of that register, as fast as possible.  With safely one sample every 200 nanoseconds, you can discern a waveform that is changing state as fast as every 400 nanoseconds (theoretically).  This really allows you to check your first waveform using Moteino, without having an oscilloscope.  And technically, you can check eight waveforms at the same time.

This method gives you some idea of what is happening, but digitally.  You cannot get the shape of the waveform exactly, but that is not necessary.  That is to say, a sine curve may give a sequence of ones and zeros very similar to that given by a square wave, or a sawtooth wave.  But you can get the period, and when it is high, and when it is low, which is what we need to figure out what is going on with an LCD.  We aren't trying to build a general-purpose oscilloscope.  The method works on AC waveforms, but be aware that if you have zero DC bias, you will be applying negative voltages (with respect to ground) to your pins.  If you don't like applying negative voltages to your pins, then you are welcome to simply put a diode on the pin so that only the positive voltages read. Negative and zero will just read zero, and that is sufficient for our purpose.  This scale's AC waveform varied from 0 volts to about 3 volts, so approximately a 1.5V bias, great since there are as such no negative voltages.  It was also great that this was a 3V scale, as it matched perfectly with Moteino's regulated voltage.  In cases where the waveform does alternate below zero, I haven't had trouble omitting the diode, but I mention this for completeness.  My chips are my own to fry, and yes, I've had my share of that (but not from this).  User beware!  The other issue there is that a diode will reduce the positive voltage by some amount, so you do have to keep in mind the voltage level of the peaks of the waveform, with respect to the threshold voltage for a digital "high".  If you get all zeros, the voltage is too low.

In the kitchen scale, I was able to reduce the pin count from sixteen to eleven.  One pin was not used by this particular LCD, and another pin had redundant information.  I could also omit three of the four control channels, since they were redundant information.  Since I knew they were phase shifted by 90 degrees, I only needed the waveform of one channel to define when the lows would occur on the other three channels.  But to find out which channels were phase shifted which way, I did the above described sampling on the four pins.  I found that given LCD pin 0 as a reference, LCD pin 1 was phase shifted 90 degrees, LCD pin 2 was 180 degrees from LCD pin 0, and LCD pin 3 was 270 degrees from LCD pin 0. 

If you draw a horizontal line across the waveform, representing the digital high threshold voltage, you can see how the ones and zeros match:


00000111000111000111111000000011100011100011111100000011

                    ---                      ---
                   |  |                     |  |
                   |  |                     |  |
     ---   ---   ---  |       ---   ---   ---  |      --
____|__|__|__|__|_____|______|__|__|__|__|_____|_____|_____threshold       
    |  |  |  |  |     |      |  |  |  |  |     |     |         
    |  |  |  |  |     |      |  |  |  |  |     |     |         
 ---   ---   ---      |   ---   ---   ---      |   ---   
|                     |  |                     |  |
|                     |  |                     |  |
-                      ---                      ---


The above is 56 samples, taken 667 microseconds apart.  I started with 250 microseconds as a value (because I had advance knowledge from the oscilloscope that the pulse width was 2 milliseconds).  With 250 us, I would get 8 samples per pulse, on which I could "definitely" count on the middle four to be valid.  Later, I optimized this to three samples per 2 ms (sometimes it is four, when the sampling falls right on the transitional states, since the pulse width is actually a bit more than 2 ms), where I throw away the first and last, leading to a sample somewhere near the middle of the pulse, and also allowing for a tolerance in time of one set of samples.  Choosing the timing of the sample this way leads to very accurate results.  If you really want to be certain you can take more samples at a faster rate, but I found this works 100% of the time, so in that sense I proved to myself that I, personally, could not be more certain.  So why would someone put more resources into it?  One answer might be comfort, and that is a perfectly valid reason.

I chose 56 samples because I was guaranteed to get two full cycles (plus a little bit for safety).  But why two cycles, when I only need one?  Well, in order to capture the one, you have to make sure you have a bit more than one so you can find the overlap in the signal.  Further, if you expand the window to two cycles, then you are guaranteed to capture one full single cycle from beginning to end.  You do not have to calculate any wrap around.  Sampling and storage seemed less expensive than all that calculation, a sort of ring buffer approach once you have found the falling edge from the highest peak you are using to indicate the cycle start.  And, in terms of time, the whole thing takes less than 40 milliseconds, and I judged that, from a practical standpoint, to be fine.

Interestingly, this scale seems to lack a voltage regulator, and creates waveforms proportional to the voltage provided by the batteries in the unmodified configuration.  So originally, I had the supplied alkaline batteries in there, and the waveforms were of the same shape, but the voltages were lower.  That yielded a different sequence:

00000000000000000000111000000000000000000000011100000000

                    ---                      ---
                   |  |                     |  |
___________________|__|_____________________|__|___________threshold
     ---   ---   ---  |       ---   ---   ---  |      --
    |  |  |  |  |     |      |  |  |  |  |     |     |         
    |  |  |  |  |     |      |  |  |  |  |     |     |         
    |  |  |  |  |     |      |  |  |  |  |     |     |         
 ---   ---   ---      |   ---   ---   ---      |   ---   
|                     |  |                     |  |
|                     |  |                     |  |
-                      ---                      ---


When I changed the scale to take the regulated 3.3V from the Moteino, the waveform scaled up and I got the first diagram presented.  Interesting!  Just in case, I needed an algorithm that would handle either case, in case that mid-high level was near the digital high threshold voltage, which it seemed to be; the original batteries were reading about 3.0 volts, so within ten percent of the regulated value.

Since we are discussing the threshold voltage, it makes sense to do a little side bar here and discuss why we detect the deep trough on the control channels and the high peaks on the information channels.  The answer is actually pretty simple, and boils down to two facts: the shape of the control channels never change, and we cannot detect variations in voltages below the digital high threshold directly (using this method).  So while we can measure and see the high peaks on the control channels, it is precisely because they have a predictable shape that we know exactly when the deep trough will occur, in relation to the high peak.  But not so on the information channels, which may take on different shapes based on which bits are on.  So we cannot really tell when they are in a deep trough, or if they are just the mid-level trough that is also below the threshold.  But we would need the deep trough to know if the bit is on when the control channel was high.  No way to tell directly.  So for that reason, we can easily tell when the the deep trough on the control channels will occur by detecting the high peaks that precede them, and then opportunistically detect information high peaks wherever they may pop up.  In this way, we are just counting on the peak positive voltages on both waveforms.

Moreover, this method allows for a bit of fault tolerance.  We may or may not get a digital high for those portions of the information channel waveform that are at the second-to-highest level.  But it does not matter, because those portions of the waveform never correspond to the instant that the control channel waveform is at its extreme low.  So precisely detecting this instant in time using the predictable control channel waveform, we can effectively discard any other data that might be considered ambiguous.
« Last Edit: November 12, 2013, 05:39:00 AM by kolumkilli »

kolumkilli

  • NewMember
  • *
  • Posts: 24
Re: Moteino Kitchen Scale
« Reply #11 on: November 11, 2013, 03:18:15 PM »
The detection algorithm I came up with amounted to this: find the longest stretch of ones in a row, then find the second longest stretch of ones in a row (it can be a tie).  Those sequences will mark the high peak before the low trough.  The zero immediately following the sequence can be considered a transitional sample, and can be discarded.  The next zero is virtually guaranteed to be very near the middle of the lowest low peak.  These two points will mark the control channel's cycle, and represent a spread of 360 degrees. 

There are 25 samples in that span (from Index 1 at 24, up to but not including Index 2 at 49), and so a 90 degree phase shift is represented by an index shift of 6.25.  But since we are dealing with integers, we can use the lowest integer that is greater than or equal to the exact value, in this case 7.  You can just get this mathematically with Index1 + (((Index2 - Index1)*N + T)/T) where N is the number of the control channel that is low at the time, and T is the total number of control channels, and N is always less than T, starting at 1.  This formula is valid for LCD panels with only three control channels (assuming they are equally phase shifted at 120 degrees).  I have not seen one with only two, but they apparently exist.  I have not seen five, but I imagine they might exist too.

channel 0 = 24 = We knew this already, it is our baseline, and is not phase-shifted.
channel 1 = 24 + (((49 - 24)*1 + 4)/4) = index 31.25, but will render 31 after integer truncation. Delta is 7.
channel 2 = 24 + (((49 - 24)*2 + 4)/4) = index 37.50 -> 37, delta is 13 from index 1 (24)
channel 3 = 24 + (((49 - 24)*3 + 4)/4) = index 43.75 -> 43, delta is 19 from index 1.

This results in 7 increments for the first phase shift, 13 for the next, and 19 for the last.

Since I ultimately had 11 pins, I was able to put six on PORTC pins (Analog 0 through 5, accessed by the input pin register PINC) and five on PORTD (Digital pins 3 through 7, accessed by the input pin register PIND) - no need to sample PINB at all!

So the pin mapping is:
A0 = Control channel 0
A1 = LCD pin 4
A2 = LCD pin 7
A3 = LCD pin 8
A4 = LCD pin 9
A5 = LCD pin 10
D3 = LCD pin 11
D4 = LCD pin 12
D5 = LCD pin 13
D6 = LCD pin 14
D7 = LCD pin 15

I created a two dimensional array:

Code: [Select]
byte samples[2][56];     // array of sampling values 

And the sampling procedure is nothing more than this:

Code: [Select]
int f=0;
while (f<56)
{
  samples[0][f] = PINC;
  samples[1][f] = PIND;
  delayMicroseconds(667);  // = 2/3 ms. three samples in 2 ms, which is the approximate pulse width.
  f++;
}

After the sample is taken, it can be analyzed.  For humans, output in the form of a diagram is useful, and can be generated by code like this:

Code: [Select]
  i=0;
  while(i<16)
  {
    f=0;
    Serial.print(i, HEX); Serial.print(": "); }}
    while (f<56)
    {
      switch(i)
      {
case 0:  if( (samples[0][f]& (1 << PINC0))) Serial.print("1"); else Serial.print("0"); break;
case 4:  if( (samples[0][f]& (1 << PINC1))) Serial.print("1"); else Serial.print("0"); break;
case 7:  if( (samples[0][f]& (1 << PINC2))) Serial.print("1"); else Serial.print("0"); break;
case 8:  if( (samples[0][f]& (1 << PINC3))) Serial.print("1"); else Serial.print("0"); break;
case 9:  if( (samples[0][f]& (1 << PINC4))) Serial.print("1"); else Serial.print("0"); break;
case 10: if( (samples[0][f]& (1 << PINC5))) Serial.print("1"); else Serial.print("0"); break;
case 11: if( (samples[1][f]& (1 << PIND3))) Serial.print("1"); else Serial.print("0"); break;
case 12: if( (samples[1][f]& (1 << PIND4))) Serial.print("1"); else Serial.print("0"); break;
case 13: if( (samples[1][f]& (1 << PIND5))) Serial.print("1"); else Serial.print("0"); break;
case 14: if( (samples[1][f]& (1 << PIND6))) Serial.print("1"); else Serial.print("0"); break;
case 15: if( (samples[1][f]& (1 << PIND7))) Serial.print("1"); else Serial.print("0"); break;
      }
      f++;
    }
    Serial.println();
    i++;
  }

There may be more efficient ways to do this but this is just printout for human eyes, is not interspersed in the sampling, and does not need to be efficient.  This code is not included in the final program, it was just used for me to visualize and decide.

Anyway, with the blank pins removed, the output looks like this:

                         Index1                   Index2
                           |                        |
                           V                        V
0: 0000011100011100011111100000001110001110001111110 0000011
4: 0011100011100011100011100001110001110001110001110 0011100
7: 0011100011100011100011100001110001110001110001110 0011100
8: 0011100011100011100011100001110001110001110001110 0011100
9: 0011100011100011100011100001110001110001110001110 0011100
A: 0011100011100011100011100001110001110001110001110 0011100
B: 0000000000000000000000000000000000000000000000000 0000000
C: 0011100011100011100011100001110001110001110001110 0011100
D: 0000011100011100011111100000001110001110001111110 0000011
E: 1100011111100011100000011110001111110001110000001 1100011
F: 0011100000011111100011100001110000001111110001110 0011100


The Index values are near the middle of the low peaks on control channel 0, as described.
Checking the values at the instant in time of Index 1, which is when control channel 0 is at its lowest, we can see this:

                          Index1                   Index2
                            |                        |
                            V                        V
0: 000001110001110001111110 0 0000011100011100011111100000011
4: 001110001110001110001110 0 0011100011100011100011100011100
7: 001110001110001110001110 0 0011100011100011100011100011100
8: 001110001110001110001110 0 0011100011100011100011100011100
9: 001110001110001110001110 0 0011100011100011100011100011100
A: 001110001110001110001110 0 0011100011100011100011100011100
B: 000000000000000000000000 0 0000000000000000000000000000000
C: 001110001110001110001110 0 0011100011100011100011100011100
D: 000001110001110001111110 0 0000011100011100011111100000011
E: 110001111110001110000001 1 1100011111100011100000011100011
F: 001110000001111110001110 0 0011100000011111100011100011100


There is one segment on, associated with control channel 0.  That is on LCD pin 14 (denoted by E in hexadecimal), input to D6 on Moteino.

As per the LCD map, this corresponds to segment 0E, which is the top segment of the rightmost seven-segment digit.  All other segments on control channel 0 are off, which are mostly the top and upper-left segments of the seven segment digits; again, refer to map.  So then we get the sample values for control channel 1, phase shifted 90 degrees (lag) which we said corresponds to a delta of 7 samples:

                         Index1                   Index2
                           |                        |
                           V123456 7                 V
0: 0000011100011100011111100000001 1 100011100011111100000011
4: 0011100011100011100011100001110 0 011100011100011100011100
7: 0011100011100011100011100001110 0 011100011100011100011100
8: 0011100011100011100011100001110 0 011100011100011100011100
9: 0011100011100011100011100001110 0 011100011100011100011100
A: 0011100011100011100011100001110 0 011100011100011100011100
B: 0000000000000000000000000000000 0 000000000000000000000000
C: 0011100011100011100011100001110 0 011100011100011100011100
D: 0000011100011100011111100000001 1 100011100011111100000011
E: 1100011111100011100000011110001 1 111100011100000011100011
F: 0011100000011111100011100001110 0 000011111100011100011100


From this we can tell segments 1D and 1E are on.  Those are the center and upper-right segments of the right most digit.

Control channel 2, shifted 13 samples:
                         Index1                   Index2
                           |                        |
                           V123456789012 3          V
0: 0000011100011100011111100000001110001 1 100011111100000011
4: 0011100011100011100011100001110001110 0 011100011100011100
7: 0011100011100011100011100001110001110 0 011100011100011100
8: 0011100011100011100011100001110001110 0 011100011100011100
9: 0011100011100011100011100001110001110 0 011100011100011100
A: 0011100011100011100011100001110001110 0 011100011100011100
B: 0000000000000000000000000000000000000 0 000000000000000000
C: 0011100011100011100011100001110001110 0 011100011100011100
D: 0000011100011100011111100000001110001 1 100011111100000011
E: 1100011111100011100000011110001111110 0 011100000011100011
F: 0011100000011111100011100001110000001 1 111100011100011100


Segments 2D and 2F are on, lower-left segment of rightmost digit, and the "ml" (milliliters, I would have written "mL" but they didn't do it that way) indicator. 

(As an aside that I cannot resist writing, this cheap scale does not magically detect the density of the substance you are measuring.  It appears to assume you are always measuring water at standard temperature and pressure, and the reading is exactly equal to the grams reading.  As it is a kitchen scale, I think it is reasonable to not assume I might be pouring in molten lead or crumbled silica aerogel, but it is in fact at least slightly incorrect most of the time.  Milk will deviate by 5%, and olive oil by around 10% in the other direction - common liquids found in the kitchen, that you might actually want to measure with this scale.  The room is likely not at standard temperature and pressure, either.  Why do they make things like this?  To taunt me, clearly.  This is strictly a weight measurement device, not volume under any conditions it likewise cannot detect.  It also is not a mass measure, so technically grams is incorrect too, but I'll go with using it on the earth, in its gravity, close enough to sea level to make that assumption not matter.  What I am not going to do is try to figure out how many Newtons or dynes of salt to use in a recipe...)


Control channel 3, shifted 19 samples:
                         Index1                   Index2
                           |                        |
                           V123456789012345678 9    V
0: 0000011100011100011111100000001110001110001 1 111100000011
4: 0011100011100011100011100001110001110001110 0 011100011100
7: 0011100011100011100011100001110001110001110 0 011100011100
8: 0011100011100011100011100001110001110001110 0 011100011100
9: 0011100011100011100011100001110001110001110 0 011100011100
A: 0011100011100011100011100001110001110001110 0 011100011100
B: 0000000000000000000000000000000000000000000 0 000000000000
C: 0011100011100011100011100001110001110001110 0 011100011100
D: 0000011100011100011111100000001110001110001 1 111100000011
E: 1100011111100011100000011110001111110001110 0 000011100011
F: 0011100000011111100011100001110000001111110 0 011100011100


Segment 3D is on - the bottom segment of the digit.
That is it!  The scale reads 2 mL.

You can also see that the phase shift calculation corresponds to a "1" that is in the middle of the sequence of 1s comprising the three or four samples during the high state of the waveform of the information channels.  That means we are hitting it at a time that is most likely to be correct, and not in or near a transition.

Note that I have been referring back to the LCD segment map, and here it is again for reference, but the thing is, I had to deduce this map.



So really it is the reverse process, where you look at the output and look at the scale.  Then you try a different reading and see what changes.  So I started with nothing, and had 0 g.  All I did was change units and got 0 mL.  I saw the one bit go off and the other on.  Check, two segments identified.  Then I put 8 grams on the scale, got one more bit, the center segment.  Add one more gram, and the 8 becomes a 9, and I lost a segment.  I found which bit went off, and thereby identified it.  And you go on this way, eliminating overlaps and narrowing it down.  The other digits followed the same pattern as the first, so it was just a matter of confirming that this was consistently true. 

To get a numerical digit out of a bunch of segments, I made a decision logic tree based on what segments are on.    Now, this scale will also display "Err" if you overload it, so the possibilities for a digit are 0 thru 9, and E and r.  E is the same as 8 without the two right side segments, and r is represented by the lower-left segment and the center segment.

For instance, if the lower-left segment is on, the digit might be 0, 2, 6, 8, E, or r. If not, it must be 1, 3, 4, 5, 7, 9 or just off entirely.  From there you whittle down the possibilities.  There are 13 possibilities, 0-9, E, r, and off, so you need 4 bifurcations to get down to a decision.  I was trying to identify routes that included as few segments as possible, in the hope that given the map I made, there might be a solution that used the minimum of four segments, and that the remaining three might all lie on a single information channel.  If so, I would be able to not sample the pin involved for the unused channel.  Alas, I was not able to do it.  I am not saying there is not a mathematical solution, just that I didn't find it.

With labels A through G given to the seven segments as per this image:



it was possible to make the decision tree given by this code:

Code: [Select]
byte InterpretSevenSegmentSet(byte A, byte B, byte C, byte D, byte E, byte F, byte G )
{
  byte digit;
  // for a given digit, we have:
  //   A
  //  F B
  //   G
  //  E C
  //   D
  // then, the algorithm is:
 
  if(E)
  {
    if(B)
    {
      if(G)
      {
        if(C) digit = 8;
        else  digit = 2;     
      }
      else    digit = 0;
    }
    else     
    {
      if(C)   digit = 6;
      else
      {
        if(A) digit = 69;  // "E"
        else  digit = 114; // "r"
      }
    }
  }
  else
  {
    if(F)
    {
      if(A)
      {
        if(B) digit = 9;
        else  digit = 5;
      }
      else    digit = 4;
    }
    else
    {
      if(A)
      {
        if(G) digit = 3;
        else  digit = 7;
      }
      else
      {
        if(B) digit = 1;
        else  digit = 0xff; // off
      }
    }
  } 
  return digit;
}

The function is easy to use.  Any return value over 9 can be checked for E, r, or off.  I could have written 255 for the return code for off, but how could I resist writing it in hex?  0xff.  It was meant to be.  The other two values are the ASCII codes for those letters.  I considered having the function return the ASCII codes for the digits as well, but as my aim was not to print these out as much as to use them numerically, it was best to return the raw numbers.  If you want ASCII codes, just add 48 to the values, for the numerical digits.

The function can be called with the bits identified by the map.  To get the rightmost digit, for instance, you can do this:

In the example above, the data set when control channel 0 is low is at index 24, that for control channel 1 is at index 31, for channel 2 it is at 37, and for 3 it is at 43.  The rightmost digit is fed by information channels 13 and 14 (hex D and E).  Those are on pins D5 and D6, which are both on PIND, values stored in samples[ 1 ][ x ], extractable with PIND5 and PIND6.

Armed with these values you can just call the function with these arguments:

Code: [Select]
byte digits = InterpretSevenSegmentSet(
samples[1][24]&(1<<PIND6), // A is 0E
samples[1][31]&(1<<PIND6), // B is 1E
samples[1][37]&(1<<PIND6), // C is 2E
samples[1][43]&(1<<PIND5), // D is 3D
samples[1][37]&(1<<PIND5), // E is 2D
samples[1][24]&(1<<PIND5), // F is 0D
samples[1][31]&(1<<PIND5)  // G is 1D
      );

Needless to say, you use variables in place of 24, 31, 37, and 43, so that as you find the cycle at different points in your sample, you can sample the four sections through the data at any point.  If you stick with the timing and number of samples you could potentially hardcode the deltas for phase shift and save a small amount of calculation, but it is not guaranteed that it will always be the same number of samples, due to where the samples happen to fall with respect to the falling edge of the waveform, and other factors.  I would do it formulaically (and did so).

So, with the safety and certainty decisions made, this set of carefully chosen samples and simple calculations work every time, consistently and robustly.  Very little time is spent sampling, and the calculations are easy because there is no averaging a lot of different sample sets to try to achieve a comparable certainty by statistical weights.  It just works, with a relatively small number of samples and not a lot of calculation.  The hard part is getting the LCD map sorted out, but once done, it's done forever (for that model LCD).  But once you've done one, you will find that others are very similar, it goes pretty quickly.

But back to sampling and calculation.  The fact that it is so minimized means that it takes very little power.  That is a good thing!  And really, it's a kitchen scale.  The samples take less than 40 milliseconds to take, to get the full reading, but does one need to sample the weight of something every 40 milliseconds?  Maybe so, but chances are this cheap kitchen scale does not have that kind of signal stability in measuring changes in its Wheatstone bridge circuit.  It is probably averaging several past sensor readings to stabilize the reading for human display, as jittery and sensitive as it may still seem.  I find that for my purposes I really only need the reading once or twice per minute, so a poll rate of once per second is more than sufficient.  The rest of the time, the MCU can simply be powered down.  If it is awake and sampling once per second, for 40 ms, it is awake only 4% of the time and retains full function.  Even better if I can made do with one sample per two seconds or more.  That said, I want to sleep the radio on a less aggressive cycle in order to maintain comm availability.  But, I don't have to sample when I wake up the MCU to further wake up the radio and check for communications.

I am still optimizing the power usage.  I can't let the scale's original circuitry power down, otherwise it re-zeros on awakening.  So when I am using it, I am actually preventing it from sleeping on its own, but it seems to be pretty stingy on power anyway.  Maybe I will make one more modification, that being to the LED that illuminates the LCD.  Simply disconnect it?  Maybe. I'm out of pins so... Well, I will have to do some measurements to see just how much power that consumes.  It's all about the interval at which I have to recharge the thing.  For now, it has a pretty big battery for the task.

And that is about all I have on this subject - but I have some other scales I am modifying in a similar manner, and I will post those here as well, as they develop.
« Last Edit: November 11, 2013, 03:36:10 PM by kolumkilli »

raymondroot

  • NewMember
  • *
  • Posts: 1
Re: Moteino Kitchen Scale
« Reply #12 on: May 04, 2014, 04:29:40 AM »
the mobile readout should be a snazzy looking watch that can tell time as well as give your readouts

berralang

  • NewMember
  • *
  • Posts: 1
  • Country: us
Re: Moteino Kitchen Scale
« Reply #13 on: March 15, 2017, 04:08:01 AM »
Wow, so professional. Do you test etekcity weight scales