I have created a processing program (Java) with which a programmer can use a GUI to make a program for an Arduino using logic function blocks. It looks like this:
Ofcourse it still needs some work. I try to make it userfriendly. The mouse above only show color if you can click a button. And a text will always appear what the mouse click will do.
I have not really found suitable function block GUIs for arduino which I also liked. So I thought it would be neat to make a simple one myself.
This 'program' generates the below functional source code. The code itself is not to be altered by humans. I use hardcoded elements like the amount of blocks, the type array and the links list.
Despite the code works I do have a small problem with the structure. It contains more information than it should. For instance, memory is used to contain the pin value which is used only by inputs and outputs.
AND and OR gates do not have input pins...
The 'delay' logic gate needs a constant and a uint32_t
to... well to create a delayed action. But again, neither the inputs, outputs, OR gates or the AND gates need a 32 bit variable.
const int nBlocks = 5 ;
enum blockTypes
{
AND = 1,
OR,
M,
DEL,
NOT,
INPUT_PIN,
OUTPUT_PIN,
} ;
const int typeArray[] = {
6, 6, 6, 2, 7, } ; // hardcoded array which tells which blocks are OR gates or AND gates etc.
typedef struct blox
{
uint8_t IN1 : 1 ; // generic for most blocks
uint8_t IN2 : 1 ; // generic for most blocks
uint8_t IN3 : 1 ; // generic for most blocks
uint8_t Q : 1 ; // generic for most blocks
uint8_t pin : 5 ; // only inputs and output types need this?
uint8_t type : 4 ; // 16 combinations
//uint32_t oldTime ; // bad idea to use this amount of memory per block if only delays need it?
//const uint32_t interval ; // perhaps couple a function pointers or obj pointer to it?
} FunctionBlock ;
FunctionBlock block [ nBlocks ] ;
In the setup all items are properly initialized. The block types are coupled to the object array, that what is input/output get their pin numbers, the hardware is initialized and and unused AND gate inputs are set to 1 etc.
void setup()
{
block[0].pin = 1 ;
block[1].pin = 2 ;
block[2].pin = 3 ;
block[3].pin = 4 ;
for( int i = 0 ; i < nBlocks ; i ++ )
{
block[i].type = typeArray[i] ;
switch( block[i].type )
{
case AND:
block[i].IN1 = block[i].IN2 = block[i].IN3 = 1 ; // force all AND gate INs to be 1 in case of unused things
break ;
case INPUT_PIN:
pinMode( block[i].pin, INPUT_PULLUP ) ;
break ;
case OUTPUT_PIN:
pinMode( block[i].pin, OUTPUT ) ;
break ;
case DEL: // idk do something clever with adding timers or something
break ;
}
}
}
The main loop is pretty straight forward. One for-loop checks all types and handles the logic for all individual function blocks. And below the for-loop there are hard coded links between the Q of one block and an IN of another block.
void loop()
{
/***************** UPDATE FUNCTION BLOCKS *****************/
for( int i = 0 ; i < nBlocks ; i ++ )
{
switch( block[i].type )
{
case AND:
block[i].Q = block[i].IN1 & block[i].IN2 & block[i].IN3 ;
break ;
case OR:
block[i].Q = block[i].IN1 | block[i].IN2 | block[i].IN3 ;
break ;
case M:
if( block[i].IN3 ) block[i].Q = 0 ; // R
else if( block[i].IN1 ) block[i].Q = 1 ; // S
break ;
case NOT:
block[i].Q = !block[i].IN2 ;
break ;
case INPUT_PIN:
block[i].Q = digitalRead( block[i].pin ) ;
break ;
case OUTPUT_PIN:
digitalWrite( block[i].pin, block[i].IN2 ) ;
break ;
// case DEL: for( int i = 0 ; i < n_blocks ; i ++ )
// {
// if( block[i].Q != block[i].IN ) // if new state changes
// {
// if( millis() - block[i].oldTime >= block[i].interval ) // keep monitor if interval has expired
// {
// block[i].Q = block[i].IN ; // if so, adopt the new state
// }
// }
// else
// {
// block[i].oldTime = millis() ; // if new state does not change, keep setting oldTime
// }
// }
// break ;
}
}
/***************** UPDATE LINKS *****************/
block[3].IN2 = block[1].Q ; // hardcoded list of all links.
block[3].IN1 = block[0].Q ;
block[3].IN3 = block[2].Q ;
block[4].IN2 = block[3].Q ;
} ;
These links correspond with the shown GUI. Blocks[0-2] are the 3 input pins, block[3] is the OR gate and block[4] is the output pin.
If something changes it will propably take several loop cycles before all blocks are updated.
As you can see, the delay block is still in comments. It does work as it is, but like I said before I do not want to waste so much memory for every last block because 1 item needs 4 bytes more memory.
Besides these elements I also want to add analog stuff. So I need to upgrade this code. (analog here being everything which can be more than '0' or '1' like analog input pin, comparators, the map() function, servo motors etc)
What do you guys think?
How do I solve that particular issue with the structure and memory. I haven't used inheritence before in C++, I did in Java. I am leaning to use classes and use inheritence but I would not know if that is the best approach here?
union
s in addition tostruct
s. With C++, why not take an object oriented approach? Blocks without an output are pointless. Blocks without inputs are boring: Start with one each. Derive blocks with more inputs and/or outputs, with different behaviour. Ponder combinatorial explosion. \$\endgroup\$