/*
Backpropagation Netz / net
optimiert für Arduino Due
Version JON 0060
(C) HaWe 2015
to do:
- save memory to SD file
- plug real physical sensors
- pre-emptive Multitasking (currently not possible for Due)
*/
#include <SPI.h>
#include <SD.h>
#include <UTFT.h>
#include <ardustdio.h>
#include <malloc.h>
#include <DueTimer.h>
//-------------------------------------------------------------------------------------
// neural net size
#define NMAXIN 108 // max number of inputs (sensors)
#define NMAXHID 20 // max number hidden layer neurons
#define NMAXOUT 20 // max number output layer neurons
#define NMAXPAT 70 // <<< max number of possibly trained patterns;
//-------------------------------------------------------------------------------------
// neural net: neurons and patterns
float WeightLIn[NMAXIN+1][NMAXHID+1];
float WeightLOut[NMAXHID+1][NMAXOUT+1];
float Input[NMAXPAT+1][NMAXIN+1];
float Target[NMAXPAT+1][NMAXOUT+1];
float Output[NMAXPAT+1][NMAXOUT+1];
//float Contxt[NMAXOUT+1]; // Jordan-net: neuron-number == output-number
float currIn[NMAXIN+1], // currently polled inputs
inbuf[NMAXIN+1]; // intermediate stored inputs for editing
float currOut[NMAXOUT+1], // currently computed net outputs
outbuf[NMAXOUT+1]; // intermediate stored outputs
int16_t NumInput = NMAXIN, NumHidden = NMAXHID, NumOutput = NMAXOUT;
int16_t NumPattern = 0; // <<< number of actually trained patterns;
int32_t epoch; // training measures
uint32_t BPTime;
float Error;
//-------------------------------------------------------------------------------------
// TFT LCD
//UTFT myGLCD(Model, SDA=MISO, SCL, CS, RESET, RS)
//UTFT myGLCD(QD220A, A2, A1, A5, A4, A3); // adjust model parameter and pins !
UTFT myGLCD(QD220A, 50, 49, 52, 0, 51); // A0->Vc (LED), A4->BoardReset
extern uint8_t SmallFont[];
#define LCDWhiteBlack() {myGLCD.setColor(255, 255, 255); myGLCD.setBackColor( 0, 0, 0);}
#define LCDNormal() {myGLCD.setColor(255, 255, 255); myGLCD.setBackColor( 0, 0, 0);}
#define LCDInvers() {myGLCD.setColor( 0, 0, 0); myGLCD.setBackColor(255, 255, 255);}
#define LCDWhiteRed() {myGLCD.setColor(255, 255, 255); myGLCD.setBackColor(255, 0, 0);}
#define LCDRedBlack() {myGLCD.setColor(255, 0, 0); myGLCD.setBackColor( 0, 0, 0);}
#define LCDYellowBlue() {myGLCD.setColor(255, 255, 0); myGLCD.setBackColor( 64, 64, 64);}
uint8_t fontwi= 8;
uint8_t fonthi=10;
int16_t LCDmaxX , LCDmaxY ; // display size
int16_t _curx_, _cury_, // last x,y cursor pos on TFT screen
_maxx_, _maxy_; // max. x,y cursor pos on TFT screen
char wspace[50]; // line of white space
void lcdcls() { myGLCD.clrScr(); _curx_ =0; _cury_ =0; }
void curlf() { _curx_=0; if( _cury_ <=(LCDmaxY-10) ) _cury_+=10; else _cury_=0; }
void lcdprintxy(int16_t x, int16_t y, char * str) {
myGLCD.print(str, x, y);
_curx_ = x + strlen(str)*fontwi;
_cury_ = y; // check for line overflow!
}
void curxy(int16_t x, int16_t y) {_curx_ = x;_cury_ = y;}
void lcdprint(char * str) {
myGLCD.print(str, _curx_, _cury_);
_curx_ = _curx_ + strlen(str)*fontwi;
//_cury_ = _cury_; // check for line overflow!
}
//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
// SD Card
#define SD_CSpin 53
File myFile;
char fname[64];
//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
// misc
#define TimerMS() millis()
#define LRAND_MAX 32767
#define srand(seed) randomSeed(seed)
#define rand() random(LRAND_MAX)
#define rando() ((float)rand()/(LRAND_MAX+1))
int32_t RSeed;
#define pswitchon(pin) (!digitalRead(pin)) // btn press for pinMode(pin, INPUT_PULLUP)
#define pswitchoff(pin) ( digitalRead(pin)) // btn press for pinMode(pin, INPUT_PULLUP)
#define pbtn(pin) (!digitalRead(pin)) // alias
//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
// user interface: button pad control pins
#define PIN_ESC 13
#define PIN_UP 12
#define PIN_OK 11
#define PIN_DN 4 // instead opt.: 6
#define PIN_LE 3 // instead opt.: 5
#define PIN_RI 2
// pins 10,9,8,7 : motor
// pins 22 - 37 : motor
// pins 49 - 51 : LCD TFT
// pin 53: SD CS
// available: 5+6, 38-48 for digital touch pins
//-------------------------------------------------------------------------------------
int16_t btnpressed() {
return ( pbtn(PIN_ESC)||pbtn(PIN_UP)||pbtn(PIN_OK)||pbtn(PI N_DN)||pbtn(PIN_LE)||pbtn(PIN_RI) );
}
//-------------------------------------------------------------------------------------
int16_t getbtn() {
int16_t choice= -1;
while (! btnpressed() ); // wait until button pad pressed
if( pbtn(PIN_ESC) ) choice = PIN_ESC;
if( pbtn(PIN_UP) ) choice = PIN_UP;
if( pbtn(PIN_OK) ) choice = PIN_OK;
if( pbtn(PIN_DN) ) choice = PIN_DN;
if( pbtn(PIN_LE) ) choice = PIN_LE;
if( pbtn(PIN_RI) ) choice = PIN_RI;
while ( btnpressed() ); // wait until button pad released
return choice;
}
//-------------------------------------------------------------------------------------
// misc. tools
int16_t toggleup(int16_t lo, int16_t hi, int16_t val ) {
if ( val < hi ) val++;
else val = lo;
return val;
}
int16_t toggledn(int16_t lo, int16_t hi, int16_t val ) {
if ( val > lo ) val--;
else val = hi;
return val;
}
//-------------------------------------------------------------------------------------
// motor control
#define MAXMOTORS 4 // max number of encoder motors at Arduino Uno=2 // Due=6 // Mega=8
// motor 0
#define pinenc0A 22 // enc0A yellow
#define pinenc0B 23 // enc0B blue
#define pinmot0d1 24 // dir0-1 <<
#define pinmot0d2 25 // dir0-2
#define pinmot0pwm 10 // pwm enable0
// motor 1
#define pinenc1A 26 // enc1A yellow
#define pinenc1B 27 // enc1B blue
#define pinmot1d1 28 // dir1-1 <<
#define pinmot1d2 29 // dir1-2
#define pinmot1pwm 9 // pwm enable1
// motor 2
#define pinenc2A 30 // enc2A yellow
#define pinenc2B 31 // enc2B blue
#define pinmot2d1 32 // dir2-1 <<
#define pinmot2d2 33 // dir2-2
#define pinmot2pwm 8 // pwm enable2
// motor 3
#define pinenc3A 34 // enc3A yellow
#define pinenc3B 35 // enc3B blue
#define pinmot3d1 36 // dir3-1 <<
#define pinmot3d2 37 // dir3-2
#define pinmot3pwm 7 // pwm enable3
//-------------------------------------------------------------------------------------
// motor bit patterns
const char COAST[]={0,0,0};
const char BREAK[]={0,0,1};
const char FWSLOW[]={0,1,0};
const char RVSLOW[]={0,1,1};
const char FWMED[]={1,0,0};
const char RVMED[]={1,0,1};
const char FWFAST[]={1,1,0};
const char RVFAST[]={1,1,1};
#define fwslow 20
#define rvslow -20
#define fwmed 60
#define rvslow -60
#define fwfast 100
#define rvfast -100
//================================================== ===================================
// SETUP ()
//================================================== ===================================
void setup() {
char sbuf[128]; // output string
Serial.begin(115200);
pinMode(PIN_ESC,INPUT_PULLUP);
pinMode(PIN_UP, INPUT_PULLUP);
pinMode(PIN_OK, INPUT_PULLUP);
pinMode(PIN_DN, INPUT_PULLUP);
pinMode(PIN_LE, INPUT_PULLUP);
pinMode(PIN_RI, INPUT_PULLUP);
myGLCD.InitLCD();
LCDmaxX=myGLCD.getDisplayXSize();
LCDmaxY=myGLCD.getDisplayYSize();
myGLCD.setFont(SmallFont);
_maxx_ = LCDmaxX / fontwi;
_maxy_ = LCDmaxX / fonthi;
memset(wspace, ' ', _maxx_);
wspace[_maxx_]='\0';
lcdcls();
sprintf(sbuf, "Serial ok, GLCD=%dx%d",LCDmaxX,LCDmaxY);
Serial.println(sbuf);
myGLCD.print (sbuf, 0, 0);
ResetNet();
RSeed = ( ((analogRead(A8)+1017)%100) * (TimerMS()% LRAND_MAX ) % LRAND_MAX );
srand(RSeed);
// # DEBUG
sprintf(sbuf, "Seed= %ld", RSeed);
myGLCD.print (sbuf, 0,10);
sprintf(sbuf, "Rand= %ld", rand() );
myGLCD.print (sbuf, 100,10);
// # DEBUG
memtest(20, "memtest: setup");
SetNetDefaultPatterns();
RefreshInputs(); // polls inputs and stores values to currIn[] array
//***********************************
//************************************************** *****************************
// debug: test different input sets
currIn[1]=1; //currIn[2]=0; currIn[3]=1; //currIn[4]=0; currIn[5]=0; currIn[6]=0; currIn[7]=0;
//************************************************** *****************************
//***********************************
ComputeMatrix(); // applies inputs to net and computes outputs to currOut[] array
TrainNet(); // basic training
}
//================================================== ===================================
//================================================== ===================================
//-------------------------------------------------------------------------------------
// analog range bit patterns
// returns 6 bits for ranges -32768...0...32767 or out of bounds (VOID==[32])
float Ana[64][8]={
{0,0,0,0,0,0,0,0}, // 0
{0,1,0,0,0,0,0,1}, // 0...1
{1,2,0,0,0,0,1,0}, // 1...2
{2,3,0,0,0,0,1,1},
{3,4,0,0,0,1,0,0},
{4,5,0,0,0,1,0,1},
{5,6,0,0,0,1,1,0},
{6,8,0,0,0,1,1,1},
{8,10,0,0,1,0,0,0},
{10,13,0,0,1,0,0,1},
{13,16,0,0,1,0,1,0},
{16,20,0,0,1,0,1,1},
{20,26,0,0,1,1,0,0},
{26,32,0,0,1,1,0,1},
{32,40,0,0,1,1,1,0},
{40,48,0,0,1,1,1,1},
{48,56,0,1,0,0,0,0},
{56,64,0,1,0,0,0,1},
{64,74,0,1,0,0,1,0},
{74,85,0,1,0,0,1,1},
{85,97,0,1,0,1,0,0},
{97,110,0,1,0,1,0,1},
{110,128,0,1,0,1,1,0},
{128,192,0,1,0,1,1,1},
{192,256,0,1,1,0,0,0},
{256,420,0,1,1,0,0,1},
{420,512,0,1,1,0,1,0},
{512,650,0,1,1,0,1,1},
{650,800,0,1,1,1,0,0},
{800,1024,0,1,1,1,0,1},
{1024,2048,0,1,1,1,1,0},
{2048,32767,0,1,1,1,1,1}, // <= SHORTMAX
{0,0,1,0,0,0,0,0}, // #32: VOID !
{0,-1,1,0,0,0,0,1}, // 0... -1
{-1,-2,1,0,0,0,1,0}, // -1...-2
{-2,-3,1,0,0,0,1,1},
{-3,-4,1,0,0,1,0,0},
{-4,-5,1,0,0,1,0,1},
{-5,-6,1,0,0,1,1,0},
{-6,-8,1,0,0,1,1,1},
{-8,-10,1,0,1,0,0,0},
{-10,-13,1,0,1,0,0,1},
{-13,-16,1,0,1,0,1,0},
{-16,-20,1,0,1,0,1,1},
{-20,-26,1,0,1,1,0,0},
{-26,-32,1,0,1,1,0,1},
{-32,-40,1,0,1,1,1,0},
{-40,-48,1,0,1,1,1,1},
{-48,-56,1,1,0,0,0,0},
{-56,-64,1,1,0,0,0,1},
{-64,-74,1,1,0,0,1,0},
{-74,-85,1,1,0,0,1,1},
{-85,-97,1,1,0,1,0,0},
{-97,-110,1,1,0,1,0,1},
{-110,-128,1,1,0,1,1,0},
{-128,-192,1,1,0,1,1,1},
{-192,-256,1,1,1,0,0,0},
{-256,-420,1,1,1,0,0,1},
{-420,-512,1,1,1,0,1,0},
{-512,-650,1,1,1,0,1,1},
{-650,-800,1,1,1,1,0,0},
{-800,-1024,1,1,1,1,0,1},
{-1024,-2048,1,1,1,1,1,0},
{-2048,-32768,1,1,1,1,1,1} // >= -SHORTMAX
};
//-------------------------------------------------------------------------------------
int16_t ana2bits(int16_t aval) { // return array index
int16_t i,j;
if(aval == 0) return 0;
else
if( aval > 32767 ) return 32; // VOID
else
if( aval < -32768 ) return 32; // VOID
else {
for (i=1; i<32; ++i) {
if ( (aval>Ana[0][i]) && (aval<=Ana[1][i]) ) return i;
}
for (i=32; i<64; ++i) {
if ( (aval<Ana[0][i]) && (aval>=Ana[1][i]) ) return i;
}
}
return 32;
}
//-------------------------------------------------------------------------------------
// mem test
extern char _end;
extern "C" char *sbrk(int i);
char *ramstart=(char *)0x20070000;
char *ramend=(char *)0x20088000;
//================================================== ===================================
void memtest(int ypos, char * str) {
char sbuf[128]; // output string
char *heapend=sbrk(0);
register char * stack_ptr asm ("sp");
struct mallinfo mi=mallinfo();
sprintf(sbuf,str);
Serial.println(); Serial.println(sbuf); myGLCD.print(sbuf, 0, ypos);
sprintf(sbuf, "Dyn.RAM used: %-10ld ", mi.uordblks);
Serial.println(sbuf); myGLCD.print(sbuf, 0, ypos+10);
sprintf(sbuf, "Prg.stat.RAM used %-10ld ", & _end - ramstart);
Serial.println(sbuf); myGLCD.print(sbuf, 0, ypos+20);
sprintf(sbuf, "Stack RAM used %-10ld ", ramend - stack_ptr);
Serial.println(sbuf); myGLCD.print(sbuf, 0, ypos+30);
sprintf(sbuf, "Free mem: %-10ld ", stack_ptr - heapend + mi.fordblks);
Serial.println(sbuf); myGLCD.print(sbuf, 0, ypos+40);
myGLCD.print(wspace, 0, ypos+50);
}
//================================================== ===================================
// net: clear, set, reset, patch
//================================================== ===================================
void clrnetbuf() {
memset(inbuf, 0, sizeof(inbuf) );
memset(currIn, 0, sizeof(currIn) );
memset(outbuf, 0, sizeof(outbuf) );
memset(currOut, 0, sizeof(outbuf) );
}
//================================================== ===================================
void ResetNet(){
memset(Input, 0, sizeof(Input) );
memset(Target, 0, sizeof(Target) );
//memset(Contxt, 0, sizeof(Contxt) );
clrnetbuf();
NumPattern=0;
}
//================================================== ===================================
// set input/target patterns manually
//================================================== ===================================
int16_t setIOpattern(int16_t patt) {
int16_t result;
char sbuf[128]; // output string
if(patt==-1) patt = CheckTestPattern(inbuf); // pattern number if known, if not: -1
if( patt > 0 ) sprintf(sbuf, " PATCH target #%-3d", patt);
else sprintf(sbuf, " NEW target #%-3d", NumPattern+1);
result=menu_setouttargets(sbuf);
if (result == -1) return (result); // break by escape btn
if (patt == -1) { // unknown => add new pattern!
SetNewPattern( inbuf, outbuf );
result=NumPattern;
}
else { // override old pattern
PatchPattern( patt, inbuf, outbuf);
result=patt;
}
return result; // return written pattern number
}
//================================================== ===================================
//================================================== ===================================
// display net inputs and target outputs
//================================================== ===================================
void DisplayNetTrainingIOs(int16_t code) {
int16_t i, o, p;
char sbuf[128]; // output string
char msg[20]="! SUCCESS !" ;
if (code==0) strcpy(msg,"calc.error");
if (code==2) strcpy(msg,"user-break");
/* print network outputs */
Serial.println(); Serial.println();
Serial.println(msg);
sprintf(sbuf, "%-6ld: Err= %9.7f", epoch, Error) ;
Serial.print("Epoch "); Serial.println(sbuf);
myGLCD.print(sbuf, 0, 20);
myGLCD.print(msg, 0, 30);
sprintf(sbuf, "NET TRAINING TIME = %ld sec \n", BPTime/1000); Serial.println(sbuf);
Serial.println();
sprintf(sbuf, "Patt. ") ; Serial.print(sbuf); myGLCD.print(sbuf, 0, 40);
for( i = 1 ; i <= NumInput ; i++ ) {
sprintf(sbuf, "Inp%-3d ", i) ; Serial.print(sbuf);
}
for( o = 1 ; o <= NumOutput ; o++ ) {
sprintf(sbuf, "Targ%-3d Outp%-3d ", o, o); Serial.print(sbuf);
}
for(int p = 1 ; p <= NumPattern ; p++ ) {
Serial.println();
sprintf(sbuf, "%3d ", p) ; Serial.print(sbuf); myGLCD.print(sbuf, 40, 40);
for( i = 1 ; i <= NumInput ; i++ ) {
sprintf(sbuf, "%5.2f ", Input[p][i]) ; Serial.print(sbuf);
}
for( o = 1 ; o <= NumOutput ; o++ ) {
sprintf(sbuf, "%5.2f %5.2f ", Target[p][o], Output[p][o]) ; Serial.print(sbuf);
}
}
Serial.println(); Serial.println();
sprintf(sbuf, "BPtrained, returning to main()...!") ; Serial.print(sbuf);
Serial.println(); Serial.println();
}
//================================================== ===================================
void displayMatrix(int16_t patt) {
int32_t btn=-1;
int16_t i, l, o, ibuf, p, lval=1, hval=NumPattern;
float fbuf;
char msgline[30], sbuf[30];
char valline[30];
p=patt;
if(p==0) p=1;
if(p>NumPattern) p=NumPattern;
do {
strcpy(msgline," 12345678901234567890");
sprintf(sbuf, "%-4d", p);
strinsert(msgline, sbuf, 0);
LCDWhiteRed();
Serial.println(msgline); lcdprintxy(0,0,msgline); curlf();
LCDNormal();
for (i=1; i<=NMAXIN; ++i) {
l=(i-1)/20;
if (i%20==1) {
sprintf(sbuf, "%4d ", i-1);
Serial.print(sbuf); lcdprint(sbuf);
}
ibuf = round(Input[p][i]);
sprintf(sbuf, "%1d", ibuf);
Serial.print(sbuf); lcdprint(sbuf);
if (i%20==0) {
Serial.println();
curlf();
}
}
Serial.println(); curlf();
strcpy(msgline,"TARG 12345678901234567890");
LCDRedBlack();
Serial.println(msgline); lcdprint(msgline); curlf();
LCDNormal();
for (i=1; i<=NMAXOUT; ++i) {
l=(i-1)/20;
if (i%20==1) {
sprintf(sbuf, "%4d ", i-1);
Serial.print(sbuf); lcdprint(sbuf);
}
ibuf = round(Target[p][i]);
sprintf(sbuf, "%1d", ibuf);
Serial.print(sbuf); lcdprint(sbuf);
if (i%20==0) {
Serial.println();
curlf();
}
}
Serial.println();
LCDYellowBlue();
sprintf(msgline, "toggle patt +-1: LEFT/RIGHT");
Serial.println(msgline); lcdprintxy(0, LCDmaxY-20, msgline);
sprintf(msgline, "+-10:UP/DN edit:OK quit:ESC");
Serial.println(msgline); lcdprintxy(0, LCDmaxY-10, msgline);
LCDNormal();
btn=getbtn();
if ( (btn==PIN_RI) || (btn==PIN_LE) ) { // browse displayed pattern
if (btn==PIN_RI) p=toggleup(lval, hval, p);
if (btn==PIN_LE) p=toggledn(lval, hval, p);
}
if ( (btn==PIN_UP) || (btn==PIN_DN) ) { // browse displayed pattern
if (btn==PIN_UP) p=toggleup(lval, hval, p+9);
if (btn==PIN_DN) p=toggledn(lval, hval, p-9);
}
if ( (btn==PIN_OK) ) { // change displayed pattern
setIOpattern(p);
}
} while ( (btn!=PIN_ESC ) );
}
//================================================== ===================================
int16_t RefreshInputs() { // polls inputs and stores values to currIn[] array
// poll digital touch values and store directly (1 Dpin = 1 input) 16 DPins == 16
// poll analog sensor values and store bit pattern for ranges (1 Apin = 6 inputs) 8 A10bit == 48
// poll motor speed and store bit pattern for speed ranges (1 Apin = 6 inputs) 4 motors == 24
// Jordan/Elman neural context neurons == feedback inputs (1...NMAXCON) 20 inputs == 20
// =108
int16_t i, cx;
cx = NMAXIN - NMAXOUT +1; // last NMAXCON inputs reserved for context neurons
//for(i=cx; i<= NMAXIN; ++i) { currIn[i] = Contxt[i] ; } //
}
//================================================== ===================================
void ComputeMatrix() { // applies currIn[] inputs to net and computes outputs (outbuf[])
int16_t i, j, o;
float SumH[NMAXHID+1];
float SumO[NMAXOUT+1];
float HidOut[NMAXHID+1];
float SumDOW[NMAXHID+1];
float lambda= 0.5; // context neuron self activation rate
memset(SumH, 0, sizeof(SumH) );
memset(SumO, 0, sizeof(SumO) );
memset(HidOut, 0, sizeof(HidOut) );
memset(SumDOW, 0, sizeof(SumDOW) );
for( j = 1 ; j <= NumHidden ; j++ ) { // compute hidden unit activations
SumH[j] = WeightLIn[0][j] ; // bias neuron
for( i = 1 ; i <= NumInput ; i++ ) {
SumH[j] += currIn[i] * WeightLIn[i][j] ;
}
HidOut[j] = 1.0/(1.0 + exp(-SumH[j])) ; // Sigmoidal Outputs
}
for( o = 1 ; o <= NumOutput ; o++ ) { // compute output unit activations and errors
SumO[o] = WeightLOut[0][o] ; // bias neuron
for( j = 1 ; j <= NumHidden ; j++ ) {
SumO[o] += HidOut[j] * WeightLOut[j][o] ;
}
currOut[o] = 1.0/(1.0 + exp(-SumO[o])) ; // Sigmoidal Outputs
}
//for( o = 1 ; o <= NumOutput ; o++ ) { // compute context neurons
// Contxt[o] = lambda*Contxt[o] + (1-lambda)*currOut[o]; // assign outputs*weight to context neurons
//}
}
//================================================== ===================================
// check for known patterns
//================================================== ===================================
int16_t CheckTestPattern(float * test) {
int16_t i, p;
for( p = 1; p <= NumPattern ; ++p) {
for( i = 1; (i <= NumInput) && ( Input[p][i] == test[i] ); ++i);
if (i > NumInput) {return p; }
}
return -1;
}
//================================================== ===================================
// patch old pattern
//================================================== ===================================
void PatchPattern(int16_t patt, float * _ibuf, float * _obuf ){
int16_t i, o;
if ( (NumPattern <= NMAXPAT) && ( NumPattern > 0) ) {
// # DEBUG
//Serial.print("NumPattern="); Serial.print(NumPattern); Serial.print(" SetNetPattern="); Serial.println(patt);
for(i=1; i<=NMAXIN; ++i) { Input[patt][i] = _ibuf[i]; }
for(o=1; o<=NMAXOUT; ++o) { Target[patt][o] = _obuf[o]; }
}
}
//================================================== ===================================
// add new pattern
//================================================== ===================================
void SetNewPattern(float * _ibuf, float * _obuf ){
int16_t i, o;
if ( (NumPattern < NMAXPAT) && ( NumPattern >= 0) ) {
NumPattern++;
// # DEBUG
//Serial.print("NumPattern="); Serial.print(NumPattern); Serial.print(" SetNetPattern="); Serial.println(patt);
for(i=1; i<=NMAXIN; ++i) { Input[NumPattern][i] = _ibuf[i]; }
for(o=1; o<=NMAXOUT; ++o) { Target[NumPattern][o] = _obuf[o]; }
}
}
// Teil 2 folgt, leider Wartezeit
Teil 2
//================================================== ===================================
// set default net patterns
//================================================== ===================================
void SetNetDefaultPatterns(){
float _ibuf[NMAXIN+1], _obuf[NMAXOUT+1];
memset( _ibuf, 0, sizeof(_ibuf) ); memset( _obuf, 0, sizeof(_obuf) );
_ibuf[1]=0; _ibuf[2]=0; _ibuf[3]=0; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=1;
_obuf[1]=1; _obuf[2]=0; _obuf[3]=0; _obuf[4]=0; _obuf[5]=0; _obuf[6]=0; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
SetNewPattern( _ibuf, _obuf);
memset( _ibuf, 0, sizeof(_ibuf) ); memset( _obuf, 0, sizeof(_obuf) );
_ibuf[1]=1; _ibuf[2]=0; _ibuf[3]=0; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
_obuf[1]=0; _obuf[2]=1; _obuf[3]=0; _obuf[4]=0; _obuf[5]=0; _obuf[6]=0; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
SetNewPattern( _ibuf, _obuf);
memset( _ibuf, 0, sizeof(_ibuf) ); memset( _obuf, 0, sizeof(_obuf) );
_ibuf[1]=0; _ibuf[2]=1; _ibuf[3]=0; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
_obuf[1]=0; _obuf[2]=0; _obuf[3]=1; _obuf[4]=0; _obuf[5]=0; _obuf[6]=0; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
SetNewPattern( _ibuf, _obuf);
memset( _ibuf, 0, sizeof(_ibuf) ); memset( _obuf, 0, sizeof(_obuf) );
_ibuf[1]=1; _ibuf[2]=1; _ibuf[3]=0; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
_obuf[1]=0; _obuf[2]=0; _obuf[3]=0; _obuf[4]=1; _obuf[5]=0; _obuf[6]=0; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
SetNewPattern( _ibuf, _obuf);
memset( _ibuf, 0, sizeof(_ibuf) ); memset( _obuf, 0, sizeof(_obuf) );
_ibuf[1]=0; _ibuf[2]=0; _ibuf[3]=1; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
_obuf[1]=0; _obuf[2]=0; _obuf[3]=0; _obuf[4]=0; _obuf[5]=1; _obuf[6]=0; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
SetNewPattern( _ibuf, _obuf);
memset( _ibuf, 0, sizeof(_ibuf) ); memset( _obuf, 0, sizeof(_obuf) );
_ibuf[1]=1; _ibuf[2]=0; _ibuf[3]=1; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
_obuf[1]=0; _obuf[2]=0; _obuf[3]=0; _obuf[4]=0; _obuf[5]=0; _obuf[6]=1; _obuf[7]=0; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
SetNewPattern( _ibuf, _obuf);
memset( _ibuf, 0, sizeof(_ibuf) ); memset( _obuf, 0, sizeof(_obuf) );
_ibuf[1]=0; _ibuf[2]=1; _ibuf[3]=1; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
_obuf[1]=0; _obuf[2]=0; _obuf[3]=0; _obuf[4]=0; _obuf[5]=0; _obuf[6]=0; _obuf[7]=1; _obuf[8]=0; _obuf[9]=0; _obuf[10]=0;
SetNewPattern( _ibuf, _obuf);
memset( _ibuf, 0, sizeof(_ibuf) ); memset( _obuf, 0, sizeof(_obuf) );
_ibuf[1]=1; _ibuf[2]=1; _ibuf[3]=1; _ibuf[4]=0; _ibuf[5]=0; _ibuf[6]=0;
_obuf[1]=0; _obuf[2]=0; _obuf[3]=0; _obuf[4]=0; _obuf[5]=0; _obuf[6]=0; _obuf[7]=0; _obuf[8]=1; _obuf[9]=0; _obuf[10]=0;
SetNewPattern( _ibuf, _obuf);
}
//================================================== ===================================
// Backpropagation Learning
//================================================== ===================================
// Backpropagation-C-Implementierung: nn.c 1.0 (C) JOHN BULLINARIA 2004
// verändert und portiert auf Arduino Sketch C: HELMUT WUNDER ("HaWe") 2015
//================================================== ===================================
int16_t TrainNet() {
char sbuf[128]; // output string
int16_t i, j, o, p, np, op, ranpat[NumPattern+1], result=0, ri, rj, ro, rp;
int32_t offset=0;
const float minerr=(1E-4)*(NMAXHID+NMAXOUT);
float SumH[NMAXPAT+1][NMAXHID+1];
float SumO[NMAXPAT+1][NMAXOUT+1];
float HidOut[NMAXPAT+1][NMAXHID+1];
float SumDOW[NMAXHID+1];
float DeltaO[NMAXOUT+1], DeltaH[NMAXHID+1];
float DeltaWeightLIn[NMAXIN+1][NMAXHID+1], DeltaWeightLOut[NMAXHID+1][NMAXOUT+1];
float derror, oerror, oderror,
eta = 0.6, // gradient descent contribution
alpha = 0.8, // 'momentum' term which effectively keeps a moving average
// of the gradient descent weight change contributions...
smallwt = 0.5; // smallwt is the maximum absolute size of your initial weights
/* The weight changes DeltaWeightIH and DeltaWeightHO are each made up of two components.
First, the eta component that is the gradient descent contribution.
Second, the alpha component that is a 'momentum' term which effectively keeps a moving average
of the gradient descent weight change contributions, and thus smoothes out the overall weight changes.
Fixing good values of the learning parameters eta and alpha is usually a matter of trial and error.
Certainly alpha must be in the range 0 to 1, and a non-zero value does usually speed up learning.
Finding a good value for eta will depend on the problem, and also on the value chosen for alpha.
If it is set too low, the training will be unnecessarily slow.
Having it too large will cause the weight changes to oscillate wildly, and can slow down or
even prevent learning altogether.
(I generally start by trying eta = 0.1 and explore the effects of repeatedly doubling or halving it.
*/
// # DEBUG
memtest(70, "memtest: TrainBPNet");
delay(1000);
lcdcls();
Serial.println(); Serial.println();
sprintf(sbuf, "NET TRAINING started") ; Serial.println(sbuf);
Serial.println();
myGLCD.print(sbuf, 0, 10);
uint32_t TimeStamp=TimerMS();
for( j = 1 ; j <= NumHidden ; j++ ) { /* initialize WeightLIn and DeltaWeightLIn */
for( i = 0 ; i <= NumInput ; i++ ) {
DeltaWeightLIn[i][j] = 0.0 ;
WeightLIn[i][j] = 2.0 * ( rando() - 0.5 ) * smallwt ;
}
}
for( o = 1 ; o <= NumOutput ; o ++ ) { /* initialize WeightLOut and DeltaWeightLOut */
for( j = 0 ; j <= NumHidden ; j++ ) {
DeltaWeightLOut[j][o] = 0.0 ;
WeightLOut[j][o] = 2.0 * ( rando() - 0.5 ) * smallwt ;
}
}
for( epoch = 0 ; epoch <= 30000 ; epoch++) { /* iterate weight updates */
for( p = 1 ; p <= NumPattern ; p++ ) { /* randomize order of individuals */
ranpat[p] = p ;
}
for( p = 1 ; p <= NumPattern ; p++) {
np = p + rando() * ( NumPattern + 1 - p ) ;
op = ranpat[p] ; ranpat[p] = ranpat[np] ; ranpat[np] = op ;
}
Error = 0.0 ;
for( np = 1 ; np <= NumPattern ; np++ ) { /* repeat for all the training patterns */
p = ranpat[np];
for( j = 1 ; j <= NumHidden ; j++ ) { /* compute hidden unit activations */
SumH[p][j] = WeightLIn[0][j] ;
for( i = 1 ; i <= NumInput ; i++ ) {
SumH[p][j] += Input[p][i] * WeightLIn[i][j] ;
}
HidOut[p][j] = 1.0/(1.0 + exp(-SumH[p][j])) ;
}
for( o = 1 ; o <= NumOutput ; o++ ) { /* compute output unit activations and errors */
SumO[p][o] = WeightLOut[0][o] ;
for( j = 1 ; j <= NumHidden ; j++ ) {
SumO[p][o] += HidOut[p][j] * WeightLOut[j][o] ;
}
Output[p][o] = 1.0/(1.0 + exp(-SumO[p][o])) ; /* Sigmoidal Outputs */
oerror = Error;
Error += 0.5 * (Target[p][o] - Output[p][o]) * (Target[p][o] - Output[p][o]) ; /* SSE */
oderror = derror; // stalling ?
derror = Error-oerror;
if ( (epoch <= 10000) && (Error>=0.5) && ((epoch-offset)>1000)
&& (abs(derror-oderror)<(minerr/1000) ) ) {
offset=epoch;
for( o = 1 ; o <= NumOutput ; o ++ ) { /* initialize WeightLOut and DeltaWeightLOut */
for( j = 0 ; j <= NumHidden ; j++ ) {
DeltaWeightLOut[j][o] = 0.0 ;
WeightLOut[j][o] = 2.0 * ( rando() - 0.5 ) * smallwt ;
}
}
if( (epoch-offset)%3000==0) {
for( j = 1 ; j <= NumHidden ; j++ ) {
for( i = 0 ; i <= NumInput ; i++ ) {
DeltaWeightLIn[i][j] = 0.0 ;
WeightLIn[i][j] = 2.0 * ( rando() - 0.5 ) * smallwt ;
}
}
}
}
DeltaO[o] = (Target[p][o] - Output[p][o]) * Output[p][o] * (1.0 - Output[p][o]) ; /* Sigmoidal Outputs, SSE */
}
for( j = 1 ; j <= NumHidden ; j++ ) { /* 'back-propagate' errors to hidden layer */
SumDOW[j] = 0.0 ;
for( o = 1 ; o <= NumOutput ; o++ ) {
SumDOW[j] += WeightLOut[j][o] * DeltaO[o] ;
}
DeltaH[j] = SumDOW[j] * HidOut[p][j] * (1.0 - HidOut[p][j]) ;
}
for( j = 1 ; j <= NumHidden ; j++ ) { /* update weights WeightLIn */
DeltaWeightLIn[0][j] = eta * DeltaH[j] + alpha * DeltaWeightLIn[0][j] ;
WeightLIn[0][j] += DeltaWeightLIn[0][j] ;
for( i = 1 ; i <= NumInput ; i++ ) {
DeltaWeightLIn[i][j] = eta * Input[p][i] * DeltaH[j] + alpha * DeltaWeightLIn[i][j];
WeightLIn[i][j] += DeltaWeightLIn[i][j] ;
}
}
for( o = 1 ; o <= NumOutput ; o ++ ) { /* update weights WeightLOut */
DeltaWeightLOut[0][o] = eta * DeltaO[o] + alpha * DeltaWeightLOut[0][o] ;
WeightLOut[0][o] += DeltaWeightLOut[0][o] ;
for( j = 1 ; j <= NumHidden ; j++ ) {
DeltaWeightLOut[j][o] = eta * HidOut[p][j] * DeltaO[o] + alpha * DeltaWeightLOut[j][o] ;
WeightLOut[j][o] += DeltaWeightLOut[j][o] ;
}
}
}
if( epoch % 10 == 0 )
{
sprintf(sbuf, "%-6ld: Err= %9.7f", epoch, Error) ;
Serial.print("Epoch "); Serial.println(sbuf);
myGLCD.print(sbuf, 0, 20);
}
if(Error < minerr) {result=1; break;} // stop learning when 'near enough'
if(pbtn(PIN_ESC)){delay(10); while(pbtn(PIN_ESC)); result=2; break;} // stop after Btn press-and-release
}
BPTime = TimerMS() - TimeStamp;
return result ;
}
//================================================== ===================================
// REPETITIVE LOOP ()
//================================================== ===================================
void loop() {
int16_t result, quit=0;
int32_t choice, btn;
char sbuf[128], spatt[20]; // output string
msg_userctrl();
Serial.println();
while(!quit) {
RefreshInputs(); // polls inputs and stores values to currIn[] array
//***********************************
//************************************************** *****************************
// debug: test different input sets
currIn[1]=1; //currIn[2]=0; currIn[3]=1; //currIn[4]=0; currIn[5]=0; currIn[6]=0; currIn[7]=0;
//************************************************** *****************************
//***********************************
ComputeMatrix(); // applies inputs to net and computes outputs to currOut[] array
curxy(0, LCDmaxY - fonthi*(6+NMAXOUT/20) );
LCDInvers();
lcdprint("12345678901234567890");
LCDNormal();
curlf();
for (int16_t o=1; o<=20; ++o) {
int16_t ibuf = round(currOut[o]);
sprintf(sbuf, "%1d", ibuf);
lcdprint(sbuf);
if (o%20==0) {
curlf();
}
}
result=CheckTestPattern(currIn);
sprintf(spatt,"det.inpatt=%3d", result);
myGLCD.print( spatt, 0, (LCDmaxY)-(3*fonthi) );
choice=0;
// check for btn press
if (btnpressed()) {
btn=getbtn();
}
else btn=-1;
// action for button press
if( btn==PIN_LE ) { // flash current inputs into buffer for processing
memcpy ( inbuf, currIn, sizeof(currIn) );
setIOpattern(-1);
}
else
if( btn==PIN_OK ) { // menu: set net input->target output pattern
choice = menu_setinpattern("Menu: Set Inputs"); // define net input pattern
if (choice != -1) setIOpattern(-1); // set related net target output pattern
}
else
if( btn==PIN_RI ) { // train net
result=TrainNet();
DisplayNetTrainingIOs(result);
msg_userctrl();
}
else
if( btn==PIN_ESC ) clrnetbuf(); // clear buffers
else
if( btn==PIN_UP ) { // main menu
choice = menu_0("menu (N/A)");
}
else
if( btn==PIN_DN ) { // # DEBUG
for (int i=1; i <= NMAXIN; ++i) {
sprintf(sbuf, " %003d", i); Serial.print(sbuf);
}
Serial.println();
for (int i=1; i <= NMAXIN; ++i) {
sprintf(sbuf, "%4.1f", currIn[i]); Serial.print(sbuf);
}
Serial.println(); Serial.println();
for (int o=1; o <= NMAXOUT; ++o) {
sprintf(sbuf, "%4.1f", currOut[o]); Serial.print(sbuf);
}
Serial.println();
Serial.println(spatt);
Serial.println(); Serial.println();
}
if( btn >=0 ) {
Serial.print("choice="); Serial.println(choice); // # DEBUG
msg_userctrl();
}
}
sprintf(sbuf, "\n\nGoodbye!\n\n") ; Serial.print(sbuf);
while(true);
}
// OK: check pattern, add new pattern, patch old pattern
// OK: learn converging and escape stalling (local min)
//================================================== ===================================
//================================================== ===================================
/*
M M EEEEEEEEEEE NN N U U
MM MM E N N N U U
M M M M E N N N U U
M M M M E N N N U U
M M M M E N N N U U
M M M M EEEEEEE N N N U U
M M M E N N N U U
M M E N N N U U
M M E N N N U U
M M E N N N U U
M M EEEEEEEEEEE N NN UUUUUUU
*/
//================================================== ===================================
// bottom line user control menu
//================================================== ===================================
void msg_userctrl() {
char sbuf[128]; // output string
Serial.println();
LCDYellowBlue();
sprintf(sbuf, "Menu:UP clr:ESC learn:RIGHT");
Serial.println(sbuf);
myGLCD.print(sbuf, 0, LCDmaxY-20);
sprintf(sbuf, "inp.patt.set:OK read:LEFT ");
Serial.println(sbuf);
myGLCD.print(sbuf, 0, LCDmaxY-10);
LCDNormal();
}
//================================================== ===================================
// LCD-TFT menu system
//================================================== ===================================
int32_t menu_0(char caption[] ) { // main menu
const int16_t MAXMSIZE = 8; // number of maximum useable menu options
const char LSIZE=20;
char astr[MAXMSIZE][LSIZE+1], // all options by all inscription string
opbuf[LSIZE+1], // option buffer
numbuf[LSIZE+1], // num buffer
last[LSIZE+1] , // end of line buffer
more[LSIZE+1] , // next line buffer
space[LSIZE+1] , // empty line buffer
bobross=0; // if to paint a beautiful new menu by beautiful colors ;)
static int16_t ch=0, maxframe=8, minframe=0; // static for re-entering the menu;
int16_t btn, i, j, lch, MAXVOPT=8;
int16_t val=0, lval=0, hval=3;
memset(last, '-', LSIZE ); last[LSIZE]='\0';
memset(more, '+', LSIZE ); more[LSIZE]='\0';
memset(space, ' ', LSIZE ); space[LSIZE]='\0';
for (i=0; i<MAXMSIZE; ++i) {
strcpy(astr[i], space );
sprintf(opbuf, "%3d", i);
strinsert(astr[i], opbuf, 0);
}
strinsert(astr[ 0], "File load -> 0", 4);
strinsert(astr[ 1], "File safe -> 0", 4);
strinsert(astr[ 2], "show patt. -> 0", 4);
strinsert(astr[ 3], "erase patt.-> 0", 4);
strinsert(astr[ 4], "option e ", 4);
strinsert(astr[ 5], "option f ", 4);
strinsert(astr[ 6], "option g ", 4);
strinsert(astr[ 7], "option h ", 4);
strinsert(astr[MAXMSIZE-1], "option:last ", 4);
if (ch<0) ch=0;
if(MAXVOPT>MAXMSIZE) {maxframe=MAXVOPT=MAXMSIZE; }
lcdcls();
LCDWhiteRed();
myGLCD.print(space, 0, 0);
myGLCD.print(caption, (20-strlen(caption))*8/2, 0);
LCDNormal();
for (i=minframe; i<maxframe; ++i) {
myGLCD.print(astr[i], 0, (i+1-minframe)*10);
}
LCDRedBlack();
if(maxframe<MAXMSIZE) myGLCD.print(more, 0, (MAXVOPT+1)*10);
else myGLCD.print(last, 0, (MAXVOPT+1)*10);
LCDInvers();
myGLCD.print(astr[ch], 0, (ch+1-minframe)*10);
LCDNormal();
do {
lch=ch;
btn=getbtn();
if ( ch < 2 ) { lval=0; hval=3;}
else if ( ch==2) { lval=0; hval=NumPattern;} // 0 == show all
else if ( ch==3) { lval=0; hval=NumPattern;} // 0 == VOID (for safety unerase)
else { lval=0; hval=0;}
if ( (btn==PIN_DN ) || (btn==PIN_UP ) ) {
val=lval;
if (btn==PIN_DN )
{
if(ch<MAXMSIZE-1) ch++;
else {
ch=0;
minframe=0;
maxframe=MAXVOPT;
if ( maxframe != MAXMSIZE) { bobross=1; goto newbob; }
}
if(ch>maxframe) {maxframe++; minframe++; bobross=1; goto newbob;}
}
else
if (btn==PIN_UP ) {
if (ch > 0) ch--;
else {
ch=MAXMSIZE-1;
maxframe=MAXMSIZE;
minframe=maxframe-MAXVOPT;
if ( maxframe != MAXMSIZE) { bobross=1; goto newbob; }
}
if(ch<minframe) {maxframe--; minframe--; bobross=1; goto newbob;}
}
newbob:
LCDNormal();
if(bobross) {
for (i=minframe; i<maxframe; ++i) {
myGLCD.print(astr[ch], 0, (i+1-minframe)*10);
}
bobross=0;
}
else
{
myGLCD.print(astr[lch], 0, (lch+1-minframe)*10);
}
LCDRedBlack();
if(maxframe<MAXMSIZE) myGLCD.print(more, 0, (MAXVOPT+1)*10);
else myGLCD.print(last, 0, (MAXVOPT+1)*10);
LCDInvers();
myGLCD.print(astr[ch], 0, (ch+1-minframe)*10);
LCDNormal();
}
if ( (btn==PIN_RI) || (btn==PIN_LE) ) { // change input buffer value
if (ch < 4) { // toggle through sub menu options
if (btn==PIN_RI) val=toggleup(lval, hval, val);
if (btn==PIN_LE) val=toggledn(lval, hval, val);
sprintf(numbuf, "%3d", val);
strcpy(opbuf, astr[ch]);
strinsert(opbuf, numbuf, LSIZE-3);
LCDInvers();
myGLCD.print(opbuf, 0, (ch+1-minframe)*10); // write line buffer = serial number + option
LCDNormal();
}
}
} while (!( (btn== PIN_OK) || (btn== PIN_ESC) )) ;
lcdcls();
if (btn==PIN_ESC) { return -1; }
if (ch==0) { ;} // File load -> val
else
if (ch==1) { ;} // File safe -> val
else
if (ch==2) { displayMatrix(val) ;} // show patt. -> val
else
if (ch==3) { ;} // erase patt.-> val
return val+(ch*10000); // return number of suboption + 10000* option
}
//================================================== ===================================
// menu: set target outputs manually
//================================================== ===================================
int8_t menu_setouttargets(char caption[]) { // set targets
const int16_t MAXMSIZE = NMAXOUT; // number of maximum useable menu options
const char LSIZE=17; // string length of line for option inscriptions
char astr[MAXMSIZE+1][LSIZE+1], // all options by all inscription string
opbuf[LSIZE+1], // option buffer
buf[LSIZE+5] =" ", // line buffer = serial number + option inscription
lbuf[LSIZE+5]="---------------------", // end of line buffer (-> last element)
more[LSIZE+5]="+ + + + + + + + + + +", // next line buffer (-> more elements)
bobross=0; // if to paint a beautiful new menu by beautiful colors ;)
int8_t btn, i, lch, MAXVOPT=10;
int8_t ch=0, maxframe=MAXVOPT, minframe=1; // static for re-entering the menu;
memcpy(outbuf, currOut, sizeof(outbuf) );
for (i=1; i<=MAXMSIZE; ++i) { // initialize all lines in inscription array
sprintf(opbuf, "Output%3d= %.0f ", i, outbuf[i] );
strcpy(astr[i], opbuf ); // inscription = option + outbuf value
}
if (ch<1) ch=1;
if(MAXVOPT>MAXMSIZE) {maxframe=MAXVOPT=MAXMSIZE; }
lcdcls();
LCDWhiteRed();
myGLCD.print(buf, 0, 0);
myGLCD.print(caption, (20-strlen(caption))*8/2, 0);
LCDNormal();
for (i=minframe; i<=maxframe; ++i) {
sprintf(buf, "%3d %s", i, astr[i]); // build line buffer = serial number + inscription
myGLCD.print(buf, 0, (i+1-minframe)*10);
}
LCDRedBlack();
if(maxframe<MAXMSIZE) myGLCD.print(more, 0, (i+1-minframe)*10);
else myGLCD.print(lbuf, 0, (i+1-minframe)*10);
LCDInvers();
sprintf(buf, "%3d %s", ch, astr[ch]);
myGLCD.print(buf, 0, (ch+1-minframe)*10);
LCDNormal();
do {
lch=ch;
btn=getbtn();
if ( (btn==PIN_DN ) || (btn==PIN_UP ) ) { // btn up/dn: dec/inc current option
if (btn==PIN_DN ) {
if(ch < MAXMSIZE) ch++;
else {
ch=1;
minframe=1;
maxframe=MAXVOPT;
if ( maxframe != MAXMSIZE) { bobross=1; goto newbob; }
}
if(ch>maxframe) {maxframe++; minframe++; bobross=1; goto newbob;} // adjust visible frame
}
else
if (btn==PIN_UP ) {
if (ch > 1) ch--;
else {
ch=MAXMSIZE;
maxframe=MAXMSIZE;
minframe=maxframe-MAXVOPT+1;
if ( minframe != 1) { bobross=1; goto newbob; }
}
if(ch<minframe) {maxframe--; minframe--; bobross=1; goto newbob;} // adjust visible frame
}
newbob: // if frame has changed: move frame, paint anew
LCDNormal();
if(bobross) {
for (i=minframe; i<=maxframe; ++i) {
sprintf(buf, "%3d %s", i, astr[i]);
myGLCD.print(buf, 0, (i+1-minframe)*10); // write all visible complete line buffers
}
bobross=0;
}
else
{
sprintf(buf, "%3d %s", lch, astr[lch]);
myGLCD.print(buf, 0, (lch+1-minframe)*10); // write old line buffer (normal)
}
LCDRedBlack();
if(maxframe<MAXMSIZE) { // write line for "more elements"
myGLCD.print(more, 0, (i+1-minframe)*10);
}
else myGLCD.print(lbuf, 0, (i+1-minframe)*10); // write line "last element"
LCDInvers();
sprintf(buf, "%3d %s", ch, astr[ch]);
myGLCD.print(buf, 0, (ch+1-minframe)*10); // write current line buffer (invers)
LCDNormal();
}
if ( (btn==PIN_RI) || (btn==PIN_LE) ) { // change input buffer value
if (btn==PIN_RI) outbuf[ch]=1;
if (btn==PIN_LE) outbuf[ch]=0;
sprintf(opbuf, "Output%3d= %.0f ", ch, outbuf[ch] );
strcpy(astr[ch], opbuf );
sprintf(buf, "%3d %s", ch, astr[ch]); // update inscription: option + outbuf value
LCDInvers();
myGLCD.print(buf, 0, (ch+1-minframe)*10); // write line buffer = serial number + inscription
LCDNormal();
}
} while (!( (btn== PIN_OK) || (btn== PIN_ESC) )) ;
if (btn==PIN_ESC) { lcdcls(); return -1; }
lcdcls();
return ch;
}
//================================================== ===================================
// menu: set inputs manually
//================================================== ===================================
int16_t menu_setinpattern(char caption[]) { // set inputs
const int16_t MAXMSIZE = NMAXIN; // number of maximum useable menu options
const char LSIZE=17; // string length of line for option inscriptions
char astr[MAXMSIZE+1][LSIZE+1], // all options by all inscription string
opbuf[LSIZE+1], // option buffer
buf[LSIZE+5] =" ", // line buffer = serial number + option inscription
lbuf[LSIZE+5]="---------------------", // end of line buffer (-> last element)
more[LSIZE+5]="+ + + + + + + + + + +", // next line buffer (-> more elements)
bobross=0; // if to paint a beautiful new menu by beautiful colors ;)
int16_t btn, i, lch, MAXVOPT=10;
static int16_t ch=0, maxframe=MAXVOPT, minframe=1; // static for re-entering the menu;
strcpy(astr[0], "static 0" );
for (i=1; i<=MAXMSIZE; ++i) { // initialize all lines in inscription array
sprintf(opbuf, "Input%3d = %.0f ", i, inbuf[i] );
strcpy(astr[i], opbuf ); // inscription = option + inbuf value
}
if (ch<1) ch=1;
if(MAXVOPT>MAXMSIZE) {maxframe=MAXVOPT=MAXMSIZE; }
lcdcls();
LCDWhiteRed();
myGLCD.print(buf, 0, 0);
myGLCD.print(caption, (20-strlen(caption))*8/2, 0);
LCDNormal();
for (i=minframe; i<=maxframe; ++i) {
sprintf(buf, "%3d %s", i, astr[i]); // build line buffer = serial number + inscription
myGLCD.print(buf, 0, (i+1-minframe)*10);
}
LCDRedBlack();
if(maxframe<MAXMSIZE) myGLCD.print(more, 0, (MAXVOPT+1)*10 );
else myGLCD.print(lbuf, 0, (MAXVOPT+1)*10 );
LCDInvers();
sprintf(buf, "%3d %s", ch, astr[ch]);
myGLCD.print(buf, 0, (ch+1-minframe)*10);
LCDNormal();
do {
lch=ch;
btn=getbtn();
if ( (btn==PIN_DN ) || (btn==PIN_UP ) ) { // btn up/dn: dec/inc current option
if (btn==PIN_DN ) {
if(ch < MAXMSIZE) ch++;
else {
ch=1;
minframe=1;
maxframe=MAXVOPT;
if ( maxframe != MAXMSIZE) { bobross=1; goto newbob; }
}
if(ch>maxframe) {maxframe++; minframe++; bobross=1; goto newbob;} // adjust visible frame
}
else
if (btn==PIN_UP ) {
if (ch > 1) ch--;
else {
ch=MAXMSIZE;
maxframe=MAXMSIZE;
minframe=maxframe-MAXVOPT+1;
if ( minframe != 1) { bobross=1; goto newbob; }
}
if(ch<minframe) {maxframe--; minframe--; bobross=1; goto newbob;} // adjust visible frame
}
newbob: // if frame has changed: move frame, paint anew
LCDNormal();
if(bobross) {
for (i=minframe; i<=maxframe; ++i) {
sprintf(buf, "%3d %s", i, astr[i]);
myGLCD.print(buf, 0, (i+1-minframe)*10); // write all visible complete line buffers
}
bobross=0;
}
else
{
sprintf(buf, "%3d %s", lch, astr[lch]);
myGLCD.print(buf, 0, (lch+1-minframe)*10); // write old line buffer (normal)
}
LCDRedBlack();
if(maxframe<MAXMSIZE) myGLCD.print(more, 0, (MAXVOPT+1)*10 );
else myGLCD.print(lbuf, 0, (MAXVOPT+1)*10 );
LCDInvers();
sprintf(buf, "%3d %s", ch, astr[ch]);
myGLCD.print(buf, 0, (ch+1-minframe)*10); // write current line buffer (invers)
LCDNormal();
}
if ( (btn==PIN_RI) || (btn==PIN_LE) ) { // change input buffer value
if (btn==PIN_RI) inbuf[ch]=1;
if (btn==PIN_LE) inbuf[ch]=0;
sprintf(opbuf, "Input%3d = %.0f ", ch, inbuf[ch] );
strcpy(astr[ch], opbuf );
sprintf(buf, "%3d %s", ch, astr[ch]); // update inscription: option + inbuf value
LCDInvers();
myGLCD.print(buf, 0, (ch+1-minframe)*10); // write line buffer = serial number + inscription
LCDNormal();
}
} while (!( (btn== PIN_OK) || (btn== PIN_ESC) )) ;
lcdcls();
if (btn==PIN_ESC) { return -1; }
return ch;
}
//================================================== ===================================
//================================================== ===================================
// end of file
- - - Aktualisiert - - -
PS,
der Code aus dem RobotC Forum stammt auch von mir, allerdings war RobotC damals dermaßen unglaublich verbugged (und dann auch noch recht teuer), dass ich anschließend zu NXC gewechselt bin.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.