Hello everyone!
Just last week I started tinkering with my new Arduino Micro, and I have several projects in mind... but I'm stuck, and I'd really appreciate some help.
I do have some background programing in C++, altough I havent really practiced in around 12 years.
The project I am working on looks something like this:
The objective is as follows:
-
Have three simultaneos analog or digital inputs.
-
Trend the value of the input in three charts
-
On the left of the chart, display the minimum and the máximum value of each chanel, although the picture is just showing placeholders at the moment
-
At the top of the screen, two values: the "Scan rate", which measures in the Loop routine how long it takes for the program to execute, and the "Frames per second", which shows how many times per second the screen is refreshed.
Now, following several tutorials and examples I was able to show the trend of one input fairly easy... but I noticed that the time it took the program to evaluate was very, very slow. somewhere around only 25 iterations per second.
I tried removing the screen logic from the Loop, and instead only triggering to a certain framerate... and the program gain a dramatic speed up.
Now, because I'd intending to show a somewat readable trend of each value on the screen, showing in only 100 pixels wide enough readings to see the input change seemed imposible... so My approach was to keep a "Long" array of 500 samples, storing the "Raw" data, and a "Short" array storing only the value to be displayed on the trend, each dot representing the average of 5 readings.
now, so far, the code as i copied it in here, compiles, and runs... BUT you will notice many lines that I've commented, and that's where I started getting frustrated. for example, this is the code I've got so far...
fair warning, I apologize for my horrendous way of commenting... and switching back and forth between english and spanish... let me know if I should fix my comments or standardize the naming to make it more readable
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
int Boton1Pin = 8;
int Boton1State = LOW;
int maxValue,ActualValue,LongTrendSize = 500,ShortTrendSize=100;
unsigned long lastFrame = 0, LastScanMillis=0;
int fps = 15; //number of screen refreshes per second
bool TriggerFrameRate(float &TrueFPS){
/////////////////////////////////////////////////////////////////////////
// use this function to return the confirmation that this specific scan,
// the routine to call for the display update can be called.
/////////////////////////////////////////////////////////////////////////
int RefreshDelay = 1000/fps;
unsigned long Imillis=0;
float Fmillis=0.0,FlastFrame=0.0,FloatFPS;
if(millis()-lastFrame >= RefreshDelay){
Imillis=millis();
Fmillis=(float) Imillis;
FlastFrame=(float) lastFrame;
FloatFPS = 1000.0 / (Fmillis-FlastFrame);
TrueFPS=FloatFPS;
lastFrame = millis();
return true;
}
else{
return false;
}
}
void ReadInputs(){
/////////////////////////////////////////////////////////////////////////
// Routine to make all the needed digital reads and analog reads,
// so the rest of the program doesn't repeat any.
/////////////////////////////////////////////////////////////////////////
Boton1State = digitalRead(Boton1Pin);
}
void PushShortTrend(byte (&trend)[100], int (&Ltrend)[500]){
/////////////////////////////////////////////////////////////////////////
// This subrutine will calculate the values for the short arrays, the arrays
//that will hold the value for the Y axis offset in the OLED screen.
/////////////////////////////////////////////////////////////////////////
int i,promedio,Total;
for(i=100;i>=2;i--){
trend[i-1]=trend[i-2];
}
// add up the newest five scans, and obtain the total
Total = Ltrend[0]+Ltrend[1]+Ltrend[2]+Ltrend[3]+Ltrend[4];
//divide by 5 to obtain the average.
promedio = Total / 5;
//because the input will be in a range from 0..1023 and the trend can
//only be 16 bits high, divide by 64 and store as the newest value.
trend[0]=promedio/64;
}
void PushLongTrend(int (&trend)[500], int NewValue){
/////////////////////////////////////////////////////////////////////////
//This subrutine will add the newest read value to the long array, and
//offset the rest of the values.
/////////////////////////////////////////////////////////////////////////
int i;
for(i=500;i>=2;i--){
trend[i-1]=trend[i-2];
}
trend[0]=NewValue;
}
int GetValue(int estado, int &valor){
/////////////////////////////////////////////////////////////////////////
// DEBUGGING SUBRUTINE. used to "emulate" analog inputs from a button...
// when the button is pressed, the input scales up, when it is released,
// it scales back down.
/////////////////////////////////////////////////////////////////////////
int output;
if(estado==LOW && valor>0){
valor--;
}
if(estado==HIGH && valor<1023){
valor++;
}
return ActualValue;
}
void DrawTrend(byte (&trend)[100], int x, int y){
/////////////////////////////////////////////////////////////////////////
// This routine will draw the trend based on the short array values.
// it will receive the "x" and "y" coordinates of the top left corner where
// the trend is to be drawn.
/////////////////////////////////////////////////////////////////////////
int i,TempInt;
for (i=0;i<100;i++){
TempInt=trend[i];
display.drawPixel(x+100-i,y+16-TempInt, WHITE);
}
//Serial.print(trend[0]); Serial.print(" ");
//Serial.print(trend[1]); Serial.print(" ");
//Serial.println(trend[2]);
}
int FindMaxVal(int (&trend)[500]){
/////////////////////////////////////////////////////////////////////////
// subrutine to find the highest value in the long array and return it.
/////////////////////////////////////////////////////////////////////////
int i,MaxVal=0;
for (i=0;i<500;i++){
if(trend[i]>MaxVal){
MaxVal=trend[i];
}
}
return MaxVal;
}
int FindMinVal(int (&trend)[500]){
/////////////////////////////////////////////////////////////////////////
// subrutine to find the lowest value in the long trend and return it
/////////////////////////////////////////////////////////////////////////
int i,MinVal=1500;
for (i=0;i<500;i++){
if(trend[i]<MinVal){
MinVal=trend[i];
}
}
return MinVal;
}
void RefreshOLED(int ScanRate, float FPS, byte (&trend1)[100], byte (&trend2)[100], byte (&trend3)[100], int Min1, int Max1, int Min2, int Max2, int Min3, int Max3){
/////////////////////////////////////////////////////////////////////////
// Subrutine to draw the full screen. it will only run when called by the Frame rate interrupt,
// we give this routine all the final data, it will clean the screen, write every value, draw every line and pixel,
// and then refresh the screen.
/////////////////////////////////////////////////////////////////////////
int testint;
testint = trend1[0];
display.clearDisplay();
display.setCursor(0,0); display.print("Scan: FPS:");
display.setCursor(30,0); display.print(ScanRate);
display.setCursor(88,0); display.print(FPS);
display.setCursor(0,16); display.print(Max1);
display.setCursor(0,24); display.print(Min1);
display.setCursor(0,32); display.print(Max2);
display.setCursor(0,40); display.print(Min2);
display.setCursor(0,48); display.print(Max3);
display.setCursor(0,56); display.print(Min3);
display.drawLine(26, 16, 26, 63, WHITE); //vertical left line, 26,16,26,63
display.drawLine(127, 16, 127, 63, WHITE); //vertical right line, 127,16,127,63
display.drawLine(27, 31, 126, 31, WHITE); //horizontal top, 27,31,126,31
display.drawLine(27, 47, 126, 47, WHITE); //horizontal middle, 27,47,126,47
display.drawLine(27, 63, 126, 63, WHITE); //horizontal bottom, 27,63,126,63
//DrawTrend(trend1, 27, 16);
//testint = trend1[0];
//display.drawPixel(27,25, WHITE);
//
//
display.display();
}
void setup() {
/////////////////////////////////////////////////////////////////////////
// setup; declaring inputs, serial and text
//
/////////////////////////////////////////////////////////////////////////
int i;
pinMode(Boton1Pin, INPUT);
Serial.begin(9600);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
for(;;);
}
delay(2000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
}
void loop() {
/////////////////////////////////////////////////////////////////////////
// Loop routine,
//
// VARIABLE DEFINITION AND INITIALIZATION
//
/////////////////////////////////////////////////////////////////////////
byte ShortTrend1[100],ShortTrend2[100],ShortTrend3[100];
int LongTrend1[500], LongTrend2[500], LongTrend3[500];
int Min1=1000,Max1=1009,Min2=2000,Max2=2009,Min3=3000,Max3=3009;
int i,ScanRate=444;
float TrueFPS=66.6;
int Accum1=0, Accum2=0, Accum3=0;
for(i=0;i<100;i++){
ShortTrend1[i]=0;
ShortTrend2[i]=0;
ShortTrend3[i]=0;
}
for(i=0;i<500;i++){
LongTrend1[i]=0;
LongTrend2[i]=0;
LongTrend3[i]=0;
}
/////////////////////////////////////////////////////////////////////////
//
// The actual Cycle starts down here.
//
/////////////////////////////////////////////////////////////////////////
while (true){
ReadInputs();
GetValue(Boton1State,Accum1);
PushLongTrend(LongTrend1,Accum1);
PushShortTrend(ShortTrend1,LongTrend1);
/////////////////////////
Max1=LongTrend1[0];
//delay(1);
////////////////////////
//Min1 = FindMinVal(LongTrend1); Max1 = FindMaxVal(LongTrend1);
//Min2 = FindMinVal(LongTrend2); Max2 = FindMaxVal(LongTrend2);
//Min3 = FindMinVal(LongTrend3); Max3 = FindMaxVal(LongTrend3);
//
//
//
//
ScanRate=millis()-LastScanMillis;
//Serial.print(LongTrend1[0]); Serial.print(" ");
//Serial.print(LongTrend1[1]); Serial.print(" ");
//Serial.println(LongTrend1[2]);
LastScanMillis=millis();
if(TriggerFrameRate(TrueFPS)){
//Serial.print(ShortTrend1[0]); Serial.print(" ");
//Serial.print(ShortTrend1[1]); Serial.print(" ");
//Serial.println(ShortTrend1[2]);
RefreshOLED(ScanRate,TrueFPS,ShortTrend1,ShortTrend2,ShortTrend3,Min1,Max1,Min2,Max2,Min3,Max3);
}
//delay (50);
}
}
The part that is really confusing me, is very punctual... the code like this "runs"... it doesn't do anything yet, but it compiles, and it shows the screen correctly.
but for example, this section, inside the while cycle in Loop:
PushShortTrend(ShortTrend1,LongTrend1);
/////////////////////////
Max1=LongTrend1[0];
//delay(1);
////////////////////////
Given what I mentioned, that is obviously wrong... my Max value should come from an evaluation, not just the newest value from the array... correct?
well, that's just the sample... the part that is thwoing me for a loop is that if I try to do ANYTHING with that array, the whole code crashes... and I have no idea why.
easiest example? If I switch the index to 1 instead of zero, as in:
Max1=LongTrend1[1];
it still compiles, it still runs... and yet, now all you can see in the screen is a frozen image that flashes every two seconds. no live data, no action from the button.
I can't understand what kind of mistake I could have done that I'm able to use a whole array, but only address the first register...
any help or suggestions is very welcome!