Saturday, 10 April 2021

Master clock / divider & multiplier - Millis() - OLED display

My third attempt at using an Arduino to make a master clock with dividers and multipliers.
This is a good beginners circuit.
 
 
My earlier attempts are here:
You can of course just buy a clock multiplier / divider, but where's the fun in that.
 
i'M USING  a basic Arduino Uno
The OLED communicates via i2c
PinWiring to Arduino Uno
Vin5V
GNDGND
SCLA5
SDAA4
 

 
The POT is wired thus:
centre (wiper) to A0
Right to GND
Left to 5V
 
The pot supplies an analog voltage which is converted into a digital
signal by the Arduino's ADCs.
Maybe an encoder would be better used??
 

 
 
The 4 LEDs
Cathode to gnd via 220 ohm resistors
Anode to pins 8, 9, 10, 11. 

I played around with the delay function, but the Millis function works much better.
 
I'm not a coder so if you can suggest any improvements, let me know.
Feel free to hack & improve the code.


The code:
 
 // &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

/*
This is part of a project to build a Synth master clock module with multipliers and dividers
Hopefully will add MIDI later.
jondent808@gmail.com 
https://djjondent.blogspot.com/2018/03/arduino-index.html
 
*/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const int ledPin =  8;// the number of the LED pin 8
int ledState = LOW;             // ledState used to set the LED 8
const int ledPin9 =  9;// the number of the LED pin 9
int ledState9 = LOW;             // ledState used to set the LED 9
const int ledPin10 =  10;// the number of the LED pin 10
int ledState10 = LOW;             // ledState used to set the LED 10
const int ledPin11 =  11;// the number of the LED pin 11
int ledState11 = LOW;             // ledState used to set the LED 11

#define MIN_BPM 20      /*write here the min BPM that you want */
 #define MAX_BPM 300     /* write here the max BPM that you want */
 #define POT A0          // the potentiometer connects to analog pin A0

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Variables
int bpm;

unsigned long previousMillis = 0;        // will store last time LED was updated
unsigned long previousMillis9 = 0;        // will store last time LED9 was updated
unsigned long previousMillis10 = 0;        // will store last time LED10 was updated
unsigned long previousMillis11 = 0;        // will store last time LED11 was updated
 
const long interval = 60000;           // interval at which to blink (milliseconds)
const long interval2 = 30000;           // interval * 2
const long interval4 = 15000;           // interval * 4
const long interval8 = 7500;           // interval * 8

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin9, OUTPUT);
  pinMode(ledPin10, OUTPUT);
  pinMode(ledPin11, OUTPUT);
  digitalWrite(ledPin,ledState);// set initial state of pin 8 LED
  digitalWrite(ledPin9,ledState9);// set initial state of pin 9 LED
  digitalWrite(ledPin10,ledState10);// set initial state of pin 10 LED
  digitalWrite(ledPin11,ledState11);// set initial state of pin 11 LED
 
 
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C
  display.clearDisplay(); // Clear the buffer.
}

void loop() {

   bpm = map(analogRead(POT), 0, 1023, MIN_BPM, MAX_BPM);  
    display.clearDisplay();
    display.setTextSize(3);
    display.setTextColor(WHITE);
    display.setCursor(0,0);
    display.println(bpm);
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.println("    BPM");
    display.display();
    
 
unsigned long currentMillis = millis();
unsigned long currentMillis9 = millis();
unsigned long currentMillis10 = millis();
unsigned long currentMillis11 = millis();

  if (currentMillis - previousMillis >= interval/bpm) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED 8 is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
 // set the LED 8 with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }

    // **********************************
    
 if (currentMillis9 - previousMillis9 >= interval2/bpm) {
    // save the last time you blinked the LED
    previousMillis9 = currentMillis9;
    
    // if the LED 9 is off turn it on and vice-versa:
    if (ledState9 == LOW) {
      ledState9 = HIGH;
    } else {
      ledState9 = LOW;
    }
    // set the LED 9 with the ledState of the variable:
    digitalWrite(ledPin9, ledState9);
  }
  // ********************************************
      
 if (currentMillis10 - previousMillis10 >= interval4/bpm) {
    // save the last time you blinked the LED
    previousMillis10 = currentMillis10;
    
    // if the LED 10 is off turn it on and vice-versa:
    if (ledState10 == LOW) {
      ledState10 = HIGH;
    } else {
      ledState10 = LOW;
    }
    // set the LED 10 with the ledState of the variable:
    digitalWrite(ledPin10, ledState10);
  }
  //*******************************************

  if (currentMillis11 - previousMillis11 >= interval8/bpm) {
    // save the last time you blinked the LED
    previousMillis11 = currentMillis11;
    
    // if the LED 11 is off turn it on and vice-versa:
    if (ledState11 == LOW) {
      ledState11 = HIGH;
    } else {
      ledState11 = LOW;
    }
    // set the LED 11 with the ledState of the variable:
    digitalWrite(ledPin11, ledState11);
}
}

// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 



 ---------------------------------
-------------------------------------

3 comments:

  1. Hi there. I'm enjoying reading about your experiments.

    In terms of things to think about with your code, here are a few ideas of things you could consider:
    * analogRead can be slow, so it might be worth only reading it once in a while rather than every time the loop() runs. But if you aren't worried about particularly fast counting frequencies, it is probably not an issue worth bothering about.
    * similarly with updating the display. You might get a faster loop updating the display on a different pass to reading the pot for example, but again only if that is important to you.
    * you could actually use a timer interrupt yourself to take your counter handling out of your loop completely and give it a more accurate "tick" if you wanted (the Arduino millis() code does this itself). The TimerOne library might help here (I talk about it a bit here: https://diyelectromusic.wordpress.com/2020/06/25/arduino-r2r-digital-audio-part-2/).
    * you could use arrays of counters/values to save cutting and pasting the timer code and then work through them one at a time in your loop.
    * you could think about how to abstract all the timer/led handling into its own function which takes a parameter to decide which led/counter to service.
    * If you are always planning to output to the LEDS as a binary counted sequence (1,2,4,8) then another option might be to write directly to the Arduino's GPIO pins with a single counter value. IO pins 8,9,10,11 are the first four bits of "PORTB" of the ATMega328, so you could set them or clear them directly by writing a 4-bit value to PORTB directly for example (see: https://www.arduino.cc/en/Reference/PortManipulation).

    Just a few ideas! Apologies if you already knew all this, I wasn't sure what your starting point for knowledge was given your "I am not a coder" comment :)

    I did some experimenting with Arduino for frequency generation, which is not dissimilar to what you are doing. I managed to get 12 tones playing at different frequencies from one Uno in the end!

    If you are interested, there are details of how I did it here: https://diyelectromusic.wordpress.com/2021/02/05/arduino-tone-polyphony/

    Best wishes,
    Kevin

    ReplyDelete
    Replies
    1. Hello Kevin, Thanks for your awesome suggestions.
      I am definitely just a beginner, so do value your suggestions very much. I will certainly try using the timerOne library.
      I was also thinking of adding the tone function -- maybe as a simple tuner ??
      Anyway, if you do have any other ideas do let me know.
      I'll up date the blog as I go along. Cheers jono

      Delete
  2. "you could use arrays of counters/values to save cutting and pasting the timer code and then work through them one at a time in your loop."
    Great idea Kevin.
    "If you are always planning to output to the LEDS as a binary counted sequence (1,2,4,8) then another option might be to write directly to the Arduino's GPIO pins with a single counter value."
    I plan to not fix the output of the leds to binary. I would still like them to be connected to the clock. Maybe some odd divisions or I might add a random function somewhere ??

    ReplyDelete