1

I'm doing that for my school project, but I have few questions before I purchase the components.

First of all, let me explain how it will work:

There will be 4x20 LCD, an Arduino and probably two potentiometers (due to the potentiometer's sensitivity). When the user turns the potentiometers, the program adds the first potentiometer's value to the other potentiometer's value, because of sensitivity issues. Then the program checks which element corresponds to the two potentiometers' values.

After that, the program shows that element's information in the LCD (like its name, symbol, neutron number, proton number, valence electrons, ...).

Questions:

  • There are around 118 elements in the periodic table. I'm not an expert with programming, but this means there will be 118 if ... else if ... statements. I can shorten the inside of the if statement by function, but there will be still 118 if statements. I'm afraid it will be over the memory (sketch size limit) limit of the Arduino. How can I shorten the code?

  • Should I buy a Mega or will a Uno be enough?

  • Will there be any problems if I connect a 5V Arduino pin to both potentiometers and 4x20 backlight? If not, how can I fix this? I don't want to use any other power source except Arduino.

  • 2
    Learn how to use arrays and PROGMEM. It's all on the Arduino Playground if you google it. – Majenko Mar 12 '16 at 10:03
  • Look into switch-case instead of ifs – Gerben Mar 12 '16 at 13:38
  • 1
    You should be able to store about 100 to 150 bytes per element on an Uno, as a PROGMEM array. That is more than a full page of your LCD. And you may want to use a rotary encoder instead of your two pots. – Edgar Bonet Mar 12 '16 at 14:29
  • 3
    @Gerben: For this usage case, an array seems more appropriate, by far, than a long switch-case. – Edgar Bonet Mar 12 '16 at 14:30
  • 2
    The clue is in the word "PROGMEM"... – Majenko Mar 12 '16 at 19:53
2

Expanding an answer from my comment on your question:

First thing is to define a struct that holds all the data you want to store and display for one element:

struct Element {
    char symbol[3];
    char name[16];
    float mass;
    char electronic_state[9];
    // etc...
};

Do not store the atomic number, as it will be deduced from the index on the array you will build. Each string in this struct (symbol, name...) should be big enough to store the longest possible string, including the final NULL terminator. The compiler will help you here: if you do not allocate enough room, it will later say something like “error: initializer-string for array of chars is too long”.

It is obviously somewhat inefficient to allocate each string the maximum possible length. I suggest you do this for the sake of simplicity, and because I do not think you should be short on flash, even on an Uno. If you really end up being short on flash, then I suggest you seriously consider jwpat7's answer. It is somewhat complicated, but likely to be significantly more efficient than mine in terms of flash usage.

Second step is to write down the periodic table as a PROGMEM array:

const Element periodic_table[] PROGMEM = {
    // symbol  name    mass      electronic state
    {"H",  "Hydrogen", 1.008,    "1s1"     },
    {"He", "Helium",   4.002602, "1s2"     },
    {"Li", "Lithium",  6.94,     "[He] 2s1"},
    // etc...
};
const size_t table_length = sizeof periodic_table / sizeof *periodic_table;

Then your program will only have to browse through this table in order to get the needed information. The only tricky part here is that you will have to copy the data to RAM before using it, for example with something like:

Element elt;  // element in RAM
memcpy_P(&elt, &periodic_table[i], sizeof elt);

I do not have an LCD, so I wrote a test program that simply dumps the table through the serial port:

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    Serial.println(F("Z  Symbol  Name  Atomic mass  El. state"));
    Serial.println(F("---------------------------------------"));
    for (size_t i = 0; i < table_length; i++) {
        Element elt;  // element in RAM
        memcpy_P(&elt, &periodic_table[i], sizeof elt);
        Serial.print(i+1);  // Z = i+1
        Serial.print(F("    "));
        Serial.print(elt.symbol);
        Serial.print(F("     "));
        Serial.print(elt.name);
        Serial.print(F("  "));
        Serial.print(elt.mass);
        Serial.print(F("  "));
        Serial.println(elt.electronic_state);
    }
    Serial.println();
    delay(1000);
}

This program only takes 4480 bytes of flash. This means I could expand the table to 118 elements and over 200 bytes of data per element before running out of flash on an Uno.

1

You probably don't need a Mega for this, but think about it as a long term investment if you want to keep messing around with the Arduino/move on to bigger projects.

Unfortunately, typing all the code out will be quite tedious. But you can make separate arrays for strings of all the element names:

https://stackoverflow.com/questions/1088622/how-do-i-create-an-array-of-strings-in-c

And you can make corresponding arrays for all of the numerical data.

Then you take the signal, do some math to figure out how you can use the signal to obtain an integer from 1-118, write that into an integer (say i), and then write name[i], symbol[i], neutnum[i], and so on to the LED screen.

The hardest part will be doing math so that the transition between two elements in terms of turning the potentiometer feels natural.

Note that if your math somehow generates 200, then name[200] will most likely write random values from memory to your LED screen. I think. If your LED screen is displaying nonsense, then that is what you should look at/debug first.

  • thx for the answer, i will probably buy a incremental rotary encoder. But if i cant set up the R.E. i will go with pots. Arrays didn't come to my mind before, but now i understand that arrays will work better. I will buy mega, i found a cheap one (Non-Clone). Thx again – Çay Öncesi Mar 13 '16 at 12:30
1

Rather than manually putting all of the periodic-table data into a program in just the right form (which would be quite tedious), you could use a program on your software development computer to read periodic-table data files and create the corresponding C-program data structures.

The python program shown below gives an example of that approach. (It contains a data table in a list of tuples, ie doesn't read data; but data like that in the tuples can be read from a file, or with a bit of programming, from a spreadsheet of periodic-table data.)

The python program opens file datatables.data.c for output, then it writes C statements into that file, which subsequently can be added into a sketch, datatables.ino, as shown later.

Python program, densconmelt.py:

#!/usr/bin/python
# From data as shown in slide 19 of 39 at
# http://www.slideshare.net/bravetiger1964/lecture-11-metals-and-its-alloys-their-crystalline-structure-and-properties

# Physical Properties of several Metals and Alloys.
#   Metal or Alloy
#     Density (kg/m3) 
#       Thermal conductivity W/(m*K) (t=20 C)
#         Melting point (Tm) (C)
adtm = [
    ('Aluminum', '2712', '204', '659'),
    ('Aluminum alloys', '7700-8700', '120-180', '462-671'),
    ('Brass-casting', '8400-8700',  '',  '990-1025'),
    ('Red Brass',     '8746', '159', '1027'),
    ('Yellow Brass',  '8470', '115', '930'),
    ('Bronze - lead', '7700-8700', '',  '850-1000'),
    ('Copper', '8930', '385', '1083'),
    ('Gold', '19320',  '318', '1063'),
    ('Pure iron',     '6800-7800', '72', '1530'),
    ('Cast Iron',     '7850', '', 'Gray 1370, Malleable 1360, White 1370'),
    ('Wrought Iron',  '7750', '58', '1450'),
    ('Lead',  '11340', '35.3', '327'),
    ('Nickel', '8800', '89', '1453'),
    ('Silver', '10490', '406', '961'),
    ('Solder 50/50 Pb/Sn', '8885', '', ''),
    ('Non-alloyed and low-alloy steel', '7850', '53.6', '1480'),
    ('Stainless Steel', '7480-8000', '12.11-45.0', '1430-1500'),
    ('Tin',  '7280', '63', '232'),
    ('Zinc', '7135', '115', '419')
    ]

locs = []
# To track current offset into datatext, and longest single string part
used = longy = 0 
with open('datatables.data.c', 'w') as fo:
    fo.write('#ifdef ADDTABLE  // Disallows separate compile\n')
    fo.write('const char datatext[] PROGMEM = \n  ')
    for i in adtm:
        #a, d, t, m = i
        #print '{:20} {:12} {:12} {:12}'.format(a, d, t, m)
        for x in i:
            locs.append(used)
            used += len(x)+1
            longy = max(longy, len(x)+1) 
            fo.write('"{}\\0" '.format(x))
        fo.write('\n  ')
    fo.write(';\nconst int offsets[] = {')
    for o in locs:
        fo.write(' {},'.format(o))
    fo.write(' {}{};\n'.format(used, '}'))
    fo.write('enum {} LONGSTR={}, ITEMS={}{};\n'.format('{', longy, len(adtm), '}'))
    fo.write('char buffy[LONGSTR];\n')
    fo.write('#endif\n')

Note, the out-commented lines

        #a, d, t, m = i
        #print '{:20} {:12} {:12} {:12}'.format(a, d, t, m)

would be an appropriate place to modify the python program so it prints out how the lines will look when displayed on the 4x20 LCD. Rather than repeatedly downloading files to your Uno or Mega just to fix lines that are a little too long, or misaligned, etc., you could make the python program show the display appearance in the same loop where it generates data structures to be included in the Arduino sketch.

Note, if you aren't familiar with the with construct, see the With Statement section of pythonforbeginners.com's Reading and Writing Files in Python page.

What the Python program generates:

Some lines from the file generated by densconmelt.py are shown below, interspersed with comments about what those lines do. Later, the whole of the file datatables.data.c is shown.

#ifdef ADDTABLE  // Disallows separate compile

That line is a preprocessor statement to keep datatables.data.c from being compiled alone. During compilations, the Arduino IDE will copy datatables.ino and datatables.data.c into a temporary work directory and will compile each of them. However, the body of datatables.data.c won't compile correctly alone (outside the sketch), so is guarded from causing compile errors by surrounding it with #ifdef ... #endif lines.

const char datatext[] PROGMEM =

That line tells the C compiler to store the value of datatext[] into program memory. It also says the value and content of datatext are constant, and datatext is an array of characters.

  "Aluminum\0" "2712\0" "204\0" "659\0" 
  "Aluminum alloys\0" "7700-8700\0" "120-180\0" "462-671\0" 
  "Brass-casting\0" "8400-8700\0" "\0" "990-1025\0" 
...
  "Tin\0" "7280\0" "63\0" "232\0" 
  "Zinc\0" "7135\0" "115\0" "419\0" 
  ;

Those lines each give the name of a metal or alloy, its density, its thermal conductivity, and its melting point. Values are strings instead of numbers, so ranges and missing values can be handled. Note that by rules of C, strings with no operators between them concatenate together. For example, the line "Tin\0" "7280\0" "63\0" "232\0" is equivalent to the line "Tin\07280\063\0232\0". Note, each \0 is a null character at the end of a substring. If space were short, one could leave out all the \0s and compute string lengths as differences of adjacent entries in offsets[].

const int offsets[] = { 0, 9, 14, ... 524, 528, 533, 538, 542, 546};

That line tells where each substring begins, within the big string datatext. This method of storing offsets and substrings takes 12 bytes per item, ie 4·2 bytes for 4 int values plus 4 bytes for null characters. There are half a dozen ways to reduce the overhead to 5 to 10 bytes per item, but the most effective simple change would be to put offsets[] in PROGMEM. In addition, one might change some fields from strings to integers or floats, which would not need null separators or offset values if placed at fixed offsets at the front of an item's record.

enum { LONGSTR=38, ITEMS=19};
char buffy[LONGSTR];
#endif

Those lines create two constants, LONGSTR and ITEMS, telling the length of the longest substring and the number of items; allocate a buffer, as long as the longest substring; and close the guarding #ifdef ... #endif.

datatables.data.c, shown whole:

#ifdef ADDTABLE  // Disallows separate compile
const char datatext[] PROGMEM = 
  "Aluminum\0" "2712\0" "204\0" "659\0" 
  "Aluminum alloys\0" "7700-8700\0" "120-180\0" "462-671\0" 
  "Brass-casting\0" "8400-8700\0" "\0" "990-1025\0" 
  "Red Brass\0" "8746\0" "159\0" "1027\0" 
  "Yellow Brass\0" "8470\0" "115\0" "930\0" 
  "Bronze - lead\0" "7700-8700\0" "\0" "850-1000\0" 
  "Copper\0" "8930\0" "385\0" "1083\0" 
  "Gold\0" "19320\0" "318\0" "1063\0" 
  "Pure iron\0" "6800-7800\0" "72\0" "1530\0" 
  "Cast Iron\0" "7850\0" "\0" "Gray 1370, Malleable 1360, White 1370\0" 
  "Wrought Iron\0" "7750\0" "58\0" "1450\0" 
  "Lead\0" "11340\0" "35.3\0" "327\0" 
  "Nickel\0" "8800\0" "89\0" "1453\0" 
  "Silver\0" "10490\0" "406\0" "961\0" 
  "Solder 50/50 Pb/Sn\0" "8885\0" "\0" "\0" 
  "Non-alloyed and low-alloy steel\0" "7850\0" "53.6\0" "1480\0" 
  "Stainless Steel\0" "7480-8000\0" "12.11-45.0\0" "1430-1500\0" 
  "Tin\0" "7280\0" "63\0" "232\0" 
  "Zinc\0" "7135\0" "115\0" "419\0" 
  ;
const int offsets[] = { 0, 9, 14, 18, 22, 38, 48, 56, 64, 78, 88, 89, 98, 108, 113, 117, 122, 135, 140, 144, 148, 162, 172, 173, 182, 189, 194, 198, 203, 208, 214, 218, 223, 233, 243, 246, 251, 261, 266, 267, 305, 318, 323, 326, 331, 336, 342, 347, 351, 358, 363, 366, 371, 378, 384, 388, 392, 411, 416, 417, 418, 450, 455, 460, 465, 481, 491, 502, 512, 516, 521, 524, 528, 533, 538, 542, 546};
enum { LONGSTR=38, ITEMS=19};
char buffy[LONGSTR];
#endif

Sketch to get item data from program memory and display it:

We've used densconmelt.py to generate datatables.data.c as above, and now will show a sketch that gets an item's data out of program memory and displays it, whenever the sketch receives an item number via Serial input.

// Sketch that accepts a number via serial input, and responds with
// two lines of data via serial output.  The reply-data consists of an
// Alloy name on the first line, and Density, Thermal conductivity,
// and Melting point on the next line.

// See http://arduiniana.org/libraries/streaming/ for Streaming library
#include <Streaming.h>
// This sets up datatext in PROGMEM; offsets[]; buffy; LONGSTR; ITEMS
#define ADDTABLE
#include "./datatables.data.c"

void setup() {
  Serial.begin(115200);         // init serial port
}

void loop() {
  Serial << "Please enter item number from 1 to " << _DEC(ITEMS) << ": ";
  int item = 0;
  while (1) {
    while (!Serial.available()) {}; // Wait for a character
    int c = Serial.read();
    if (c == '\n') break;
    item = 10*item + c - '0';
  }
  if (item < 1 || item > ITEMS) {
    Serial << item << endl << item << " is out of range -- try again.\n";
    return;
  }
  // Display specified item's title on first line
  strcpy_P(buffy, datatext + offsets[item*4-4]);
  Serial << item << endl << "Item #" << item << ", " << buffy << endl;
  // Show other data items on next line
  strcpy_P(buffy, datatext + offsets[item*4-3]);
  Serial << "Density " << buffy;
  strcpy_P(buffy, datatext + offsets[item*4-2]);
  Serial << "   Thermal cond. " << buffy;
  strcpy_P(buffy, datatext + offsets[item*4-1]);
  Serial << "   Melt@ " << buffy << " C" << endl << endl;
}

Note, for a description of strcpy_P() arguments, see nongnu.org's avr/pgmspace.h : Program Space Utilities page.

See arduino.cc's PROGMEM page for a cursory overview of PROGMEM.

See avrfreaks.net's GCC and the PROGMEM Attribute page for detailed coverage of PROGMEM, and see Nick Gammon's Putting constant data into program memory (PROGMEM) page as well.

Example of program output, as in Serial Monitor box:

Following is an example of program output as shown in the Serial Monitor box after I typed several numbers into the input line and pressed enter after each. (Note, at the bottom of the Serial Monitor box, set Line Ending to Newline and data rate to 115200 bps.)

Please enter item number from 1 to 19: 18
Item #18, Tin
Density 7280   Thermal cond. 63   Melt@ 232 C

Please enter item number from 1 to 19: 19
Item #19, Zinc
Density 7135   Thermal cond. 115   Melt@ 419 C

Please enter item number from 1 to 19: 20
20 is out of range -- try again.
Please enter item number from 1 to 19: -2220
-2220 is out of range -- try again.
Please enter item number from 1 to 19: 2
Item #2, Aluminum alloys
Density 7700-8700   Thermal cond. 120-180   Melt@ 462-671 C

Please enter item number from 1 to 19: 0
0 is out of range -- try again.
Please enter item number from 1 to 19: 5
Item #5, Yellow Brass
Density 8470   Thermal cond. 115   Melt@ 930 C

Please enter item number from 1 to 19: 

The sketch echoes the user-input item number twice, so it appears at the end of each input solicitation as well as in line labels like “Item #5, Yellow Brass”.

  • That's seems complicated. I got the idea but i don't know anything about python syntaxs, statements..., and i can't carry an external device except arduino. just the arduino and electrical components. Thx for the idea though – Çay Öncesi Mar 13 '16 at 12:41
  • 1
    @ÇayÖncesi, the sketch uses Serial just for illustration – you would substitute a rotary encoder or a pot for input, and an LCD for output. Even if you don't use a development-system preprocessor (with a code-generator written in python, C, or whatever language you prefer) you can organize your Arduino code in a manner similar to that illustrated. – James Waldby - jwpat7 Mar 13 '16 at 18:08
0

i think you might have to buy a mega(probably more expensive!)

i also fear someone putting countless nights into coding (preferably on pc for me, because 1: sketch is only available as an ancient version on raspberrypi. & 2: easier transfer of code.)

  • i will buy mega, for this project and for future – Çay Öncesi Mar 13 '16 at 12:36
  • Could you please explain why Mega would be a better choice? – Avamander Mar 26 '16 at 23:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.