Introduction: Arduino TFT Rainbow Noise Display

We created this rainbow project using various 'noise' techniques, which create controlled random effects. By adding some colour, a rainbow effect could be produced. It uses an Arduino Nano and an 128x128 OLED display. We displayed the effects using the TFT library. We also used some miscellaneous components such as a bread board and a few wires.

Step 1: Wiring

The most basic task was the wiring of the OLED to the Arduino. We connected the GND and VCC to the respective busses on the bread board; SCL to digital pin 13; SDA to digital pin 11; RES to digital pin 8; DC to digital pin 9; CS to digital pin 10 and finally BL to 3.3V on the Arduino. Using the 5v and GND pins from the Arduino we were able to power the entire bread board.

Step 2: Smooth Noise

After initializing the requirements for the TFT display. To create the smooth noise effect, we first needed a basic noise function. This returns a relatively random value between 0 and 1 based on the x and y values passed in. It is important to note that a computer can never produce a truly random result, and this randomness is merely achieved by changing the number as much as possible, hence the very large numbers in the equation.

<p>float noise (int x, int y)<br>{
  int n;
  n = x + y * 57;
  n += (n << 13) ^ n;
  return (1.0 - ( (n * ((n * n * 15731) + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
}</p>

We then 'smooth' the noise with another function. This is achieved by producing a value based not only on the result from the coordinate passed into the function, but also the surrounding coordinates. As a result of this, coordinates nearby to each other produce a similar value.

<p>float smoothNoise(float x, float y)<br>{
  float fractX = x - (int) x;
  float fractY = y - (int) y;
  int x1 = ((int) (x) + noiseWidth) % noiseWidth;
  int y1 = ((int) (y) + noiseHeight) % noiseHeight;
  int x2 = (x1 + noiseWidth - 1) % noiseWidth;
  int y2 = (y1 + noiseHeight - 1) % noiseHeight;
  float value = 0.0f;
  value += fractX * fractY * noise(x1, y1);
  value += (1 - fractX) * fractY * noise(x2, y1);
  value += fractX * (1 - fractY) * noise(x1, y2);
  value += (1 - fractX) * (1 - fractY) * noise(x2, y2);
  return value;
}</p>

Step 3: Effects Using Smooth Noise

With this we created two effects. To do this, we looped through each pixel on the OLED and took a random noise value based on the x and y coordinates of these pixels. The first of these effect we produced by using the generated value to pick a colour, and coloured that pixel with the aforementioned colour. The second effect was produced in a similar way, but we also multiplied the colour by the generated noise value. This gave the pattern a more shaded effect. The code used is shown below:

<p>void Noise2n3(bool Noisy)<br>{
  for (int y = 0; y < noiseHeight; y++)
  {   
    for (int x = 0; x < noiseWidth; x++)
    {
      float noise = smoothNoise(x / 16.0f, y / 16.0f);
      int absNoise = floor(abs(noise) * 10);
      if (absNoise > 8) absNoise = 8;
        
      if (Noisy) setNoisyColour(colours[absNoise], noise);
      else setBlockColour(colours[absNoise]);
     
      TFTscreen.point(x, y);
    }
  }
}
void setNoisyColour(Colour colour, float noise)
{
  TFTscreen.stroke(colour.red * noise, colour.green * noise, colour.blue * noise);
}
void setBlockColour(Colour colour)
{
  TFTscreen.stroke(colour.red, colour.green, colour.blue);
}</p>

Step 4: Random Gradient Effects

There are two effects that produce a random gradient. The first effect places the pixels in relation to their rgb colour, slowly rendering a gradient pattern to the screen. The second uses the same coloured pixels as the first, but places them in a fixed order, creating a diagonal gradient along the screen.

Here is the first (based on the colours):

<p>void Noise1()<br>{
  for(int z = 0; z < 3; z++) {
    TFTscreen.background(0, 0, 0);
    int CurrentColour[3][3] = {{64, 35, 26}, {24, 64, 34}, {20, 18, 64}};
    R = CurrentColour[z][0];
    G = CurrentColour[z][1];
    B = CurrentColour[z][2];
    for(int x = 0; x < 128; x++) {
      for(int y = 0; y < 128; y++) {
        int R_Lower = R - ((x + y) / 4);
        if(R_Lower < 1) {
            R_Lower = 0;
         }
         int R_Higher = R + ((x + y) / 4);
         if(R_Higher >= 255) {
            R_Higher = 254;
          }
          int R_Offset = random(R_Lower, R_Higher);
  
          int G_Lower = G - ((x + y) / 4);
          if(G_Lower < 1) {
            G_Lower = 0;
          }
         int G_Higher = G + ((x + y) / 4);
         if(G_Higher >= 255) {
           G_Higher = 254;
         }
         int G_Offset = random(G_Lower, G_Higher);
  
         int B_Lower = B - ((x + y) / 4);
         if(B_Lower < 1) {
           B_Lower = 0;
         }
         int B_Higher = B + ((x + y) / 4);
         if(B_Higher >= 255) {
           B_Higher = 254;
         }
         int B_Offset = random(B_Lower, B_Higher);
         int mult = 2;
         if (z == 1) mult = 1;
          
         TFTscreen.stroke(R_Offset * mult, G_Offset * mult, B_Offset * mult);
         TFTscreen.point((R_Offset * (B_Offset / 32)), (G_Offset * (B_Offset / 32)));
         TFTscreen.point((G_Offset * (B_Offset / 32)), (R_Offset * (B_Offset / 32)));
         TFTscreen.point((B_Offset * (G_Offset / 32)), (R_Offset * (G_Offset / 32)));
      }
    }
  }
}</p>

And the second (the more orderly effect):

<p>void Noise4()<br>{
  for(int z = 0; z < 3; z++) {
    TFTscreen.background(0, 0, 0);
    int CurrentColour[3][3] = {{64, 35, 26}, {24, 64, 34}, {20, 18, 64}};
    R = CurrentColour[z][0];
    G = CurrentColour[z][1];
    B = CurrentColour[z][2];
    for(int x = 0; x < 128; x++) {
      for(int y = 0; y < 128; y++) {
        int R_Lower = R - ((x + y) / 4);
        if(R_Lower < 1) {
            R_Lower = 0;
         }
         int R_Higher = R + ((x + y) / 4);
         if(R_Higher >= 255) {
            R_Higher = 254;
          }
          int R_Offset = random(R_Lower, R_Higher);
  
          int G_Lower = G - ((x + y) / 4);
          if(G_Lower < 1) {
            G_Lower = 0;
          }
         int G_Higher = G + ((x + y) / 4);
         if(G_Higher >= 255) {
           G_Higher = 254;
         }
         int G_Offset = random(G_Lower, G_Higher);
  
         int B_Lower = B - ((x + y) / 4);
         if(B_Lower < 1) {
           B_Lower = 0;
         }
         int B_Higher = B + ((x + y) / 4);
         if(B_Higher >= 255) {
           B_Higher = 254;
         }
         int B_Offset = random(B_Lower, B_Higher);
         int mult = 2;
         if (z == 1) mult = 1;
          
         TFTscreen.stroke(R_Offset * mult, G_Offset * mult, B_Offset * mult);
         TFTscreen.point(x, y);
      }
    }
  }
}</p><br>

Step 5: The Final Result

In the end, we combined these effects into a sort of 'slideshow' of rainbows. To achieve this, we simply called each function after the other in a while loop:

<p>while (true)<br>  {
    Noise2n3(false);
    Noise2n3(true);
    TFTscreen.background(0, 0, 0);
    Noise1();
    Noise4();
  }</p>
Colors of the Rainbow Contest

Participated in the
Colors of the Rainbow Contest