Pov Bike Display

Hi i am following this tutorial:

using the same wiring and so
although i added a led to indicate the hall sensor being triggered and an hall sensor
the reason is for mw to be able to make this device interactive and in the end ride on a trainer on a show in my art school

hope this code thingy worked
so, what is the problem? everything seems to be logical dn working.. but yet i am bothered by that the time it takes to "show" an image that is 100 pixel wide is 1 second!
i think there is something i am missing.. maybe there is a limit of how long a picture can be for this work..
maybe my code is slow..
this is the part i am asking for help
because in 27kmh the bike is around 7.5m/s and that is a revolution in 200 milliseconds ( if i got this right ) thanks a lot and sorry if the topic/forum is not tight, will fix and edit if needed
thanks again

the code is added:

// SPDX-FileCopyrightText: 2019 Phillip Burgess for Adafruit Industries
//
// SPDX-License-Identifier: MIT

/*-----------------------------------------------------------------------
  POV LED bike wheel sketch.  Uses the following Adafruit parts:

  - Pro Trinket 5V (www.adafruit.com/product/2000)
    (NOT Trinket or 3V Pro Trinket)
  - Waterproof 3xAA battery holder with on/off switch (#771)
  - 144 LED/m DotStar strip (#2328 or #2329) ONE is enough for
    both sides of one bike wheel
  - Tactile switch button (#1119) (optional)

  Needs Adafruit_DotStar library: github.com/adafruit/Adafruit_DotStar

  Full instructions: https://learn.adafruit.com/bike-wheel-pov-display

  This project is based on Phil B's Genesis Poi:
  learn.adafruit.com/genesis-poi-dotstar-led-persistence-of-vision-poi
  and has been adapted to the Pro Trinket to accomodate more and larger
  images than Trinket.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Phil Burgess / Paint Your Dragon for Adafruit Industries.
  MIT license, all text above must be included in any redistribution.
  See 'COPYING' file for additional notes.
  ------------------------------------------------------------------------*/

#include <Arduino.h>
#include <Adafruit_DotStar.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <SPI.h> // Enable this line on Pro Trinket

#ifdef __AVR_ATtiny85__
typedef uint8_t  line_t; // Max 255 lines/image on Trinket
#else
typedef uint16_t line_t; // Bigger images OK on other boards
#endif
// CONFIGURABLE STUFF ------------------------------------------------------
#include "graphics.h" // Graphics data is contained in this header file.
// It's generated using the 'convert.py' Python script.  Various image
// formats are supported, trading off color fidelity for PROGMEM space
// (particularly limited on Trinket).  Handles 1-, 4- and 8-bit-per-pixel
// palette-based images, plus 24-bit truecolor.  1- and 4-bit palettes can
// be altered in RAM while running to provide additional colors, but be
// mindful of peak & average current draw if you do that!  Power limiting
// is normally done in convert.py (keeps this code relatively small & fast).
// 1/4/8/24 were chosen because the AVR can handle these operations fairly
// easily (have idea for handing arbitrary bit depth w/328P, but this margin
// is too narrow to contain).
// Ideally you use hardware SPI as it's much faster, though limited to
// specific pins.  If you really need to bitbang DotStar data & clock on
// different pins, optionally define those here:
#define LED_DATA_PIN  11
#define LED_CLOCK_PIN 13
#define HALL_SENSOR_PIN 3
#define LED_HALLPPIN 12
volatile bool ledState = false;
// Select from multiple images using tactile button (#1489) between pin and
// ground.  Requires suitably-built graphics.h file w/more than one image.
//#define SELECT_PIN 3
// Optional feature -- not enabled here, no space -- a vibration switch
// (aligned perpendicular to leash) is used as a poor man's accelerometer.
// Poi then lights only when moving, saving some power.  The 'fast'
// vibration switch is VERY sensitive and will trigger at the slightest
// bump, while the 'medium' switch requires a certain spin rate which may
// not trigger if you're doing mellow spins.  Neither is perfect.  To leave
// that out and simply have the poi run always-on, comment out this line:
//#define MOTION_PIN 2
// Another optional feature not enable due to physical size -- powering down
// DotStars when idle conserves more battery.  Use a PNP transistor (e.g.
// 2N2907) (w/220 Ohm resistor to base) as a 'high side' switch to DotStar
// +V.  DON'T do this NPN/low-side, may damage strip.  MOTION_PIN must also
// be defined to use this (pointless without).
//#define POWER_PIN 4
#define SLEEP_TIME 2000   // Not-spinning time before sleep, in milliseconds
// Empty and full thresholds (millivolts) used for battery level display:
#define BATT_MIN_MV 3350  // Some headroom over battery cutoff near 2.9V
#define BATT_MAX_MV 4000  // And little below fresh-charged battery near 4.1V
// These figures are based on LiPoly cell and will need to be tweaked for
// 3X NiMH or alkaline batteries!
boolean autoCycle = true; // Set to true to cycle images by default
#define CYCLE_TIME 30000     // Time, in seconds, between auto-cycle images

boolean startedImage = false; // Flag indicating if an image has started
boolean measuredImage = false; // Flag indicating if an image has been measured
uint32_t startedImageTime, finishedImage, duration, startTime, timeItTakesImage, delaytime = 0;
uint32_t s = millis();
// -------------------------------------------------------------------------
#if defined(LED_DATA_PIN) && defined(LED_CLOCK_PIN)
// Older DotStar LEDs use GBR order.  If colors are wrong, edit here.
Adafruit_DotStar strip = Adafruit_DotStar(NUM_LEDS,
                         LED_DATA_PIN, LED_CLOCK_PIN, DOTSTAR_BRG);
#else
Adafruit_DotStar strip = Adafruit_DotStar(NUM_LEDS, DOTSTAR_BGR);
#endif
void     imageInit(void);
uint16_t readVoltage(void);
#ifdef MOTION_PIN
void     sleep(void);
#endif
void setup() {
  Serial.begin(9600);
  pinMode(HALL_SENSOR_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(HALL_SENSOR_PIN), isr, FALLING);
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000L)
  clock_prescale_set(clock_div_1);   // Enable 16 MHz on Trinket
#endif

#ifdef POWER_PIN
  pinMode(POWER_PIN, OUTPUT);
  digitalWrite(POWER_PIN, LOW); // Power-on LED strip
#endif
  strip.begin();                // Allocate DotStar buffer, init SPI
  strip.clear();                // Make sure strip is clear
  strip.show();                 // before measuring battery

  // Display battery level bargraph on startup.  It's just a vague estimate
  // based on cell voltage (drops with discharge) but doesn't handle curve.
  uint16_t mV  = readVoltage();
  uint8_t  lvl = (mV >= BATT_MAX_MV) ? NUM_LEDS : // Full (or nearly)
                 (mV <= BATT_MIN_MV) ?        1 : // Drained
                 1 + ((mV - BATT_MIN_MV) * NUM_LEDS + (NUM_LEDS / 2)) /
                 (BATT_MAX_MV - BATT_MIN_MV + 1); // # LEDs lit (1-NUM_LEDS)
  for (uint8_t i = 0; i < lvl; i++) {             // Each LED to batt level...
    uint8_t g = (i * 5 + 2) / NUM_LEDS;           // Red to green
    strip.setPixelColor(i, 4 - g, g, 0);
    strip.show();                                 // Animate a bit
    delay(250 / NUM_LEDS);
  }
  delay(1500);                                    // Hold last state a moment
  strip.clear();                                  // Then clear strip
  strip.show();

  imageInit(); // Initialize pointers for default image

#ifdef SELECT_PIN
  pinMode(SELECT_PIN, INPUT_PULLUP);
#endif
#ifdef MOTION_PIN
  pinMode(MOTION_PIN, INPUT_PULLUP);
  sleep();     // Sleep until motion detected
#endif
}

// GLOBAL STATE STUFF ------------------------------------------------------

uint32_t lastImageTime = 0L; // Time of last image change
#ifdef MOTION_PIN
uint32_t prev          = 0L; // Used for sleep timing
#endif
uint8_t  imageNumber   = 0,  // Current image being displayed
         imageType,          // Image type: PALETTE[1,4,8] or TRUECOLOR
         *imagePalette,       // -> palette data in PROGMEM
         *imagePixels,        // -> pixel data in PROGMEM
         palette[16][3];     // RAM-based color table for 1- or 4-bit images
line_t   imageLines,         // Number of lines in active image
         imageLine;          // Current line number in image
#ifdef SELECT_PIN
uint8_t  debounce      = 0;  // Debounce counter for image select pin
#endif

void imageInit() { // Initialize global image state for current imageNumber
  imageType    = pgm_read_byte(&images[imageNumber].type);
#ifdef __AVR_ATtiny85__
  imageLines   = pgm_read_byte(&images[imageNumber].lines);
#else
  imageLines   = pgm_read_word(&images[imageNumber].lines);
#endif
  imageLine    = 0;
  imagePalette = (uint8_t *)pgm_read_word(&images[imageNumber].palette);
  imagePixels  = (uint8_t *)pgm_read_word(&images[imageNumber].pixels);
  // 1- and 4-bit images have their color palette loaded into RAM both for
  // faster access and to allow dynamic color changing.  Not done w/8-bit
  // because that would require inordinate RAM (328P could handle it, but
  // I'd rather keep the RAM free for other features in the future).
  if (imageType == PALETTE1)      memcpy_P(palette, imagePalette,  2 * 3);
  else if (imageType == PALETTE4) memcpy_P(palette, imagePalette, 16 * 3);
  lastImageTime = millis(); // Save time of image init for next auto-cycle
}
void nextImage(void) {
  if (++imageNumber >= NUM_IMAGES) imageNumber = 0;
  imageInit();
}
void isr() {
  uint32_t now = millis();
  duration = now - startTime;
  startTime = now;
  ledState = !ledState; // Toggle LED state
  digitalWrite(LED_HALLPPIN, ledState);

}
// MAIN LOOP ---------------------------------------------------------------

void loop() {

  uint32_t t = millis();               // Current time, milliseconds
#ifdef MOTION_PIN
  // Tried to do this with watchdog timer but encountered gas pains, so...
  if (!digitalRead(MOTION_PIN)) {      // Vibration switch pulled down?
    prev = t;                          // Yes, reset timer
  } else if ((t - prev) > SLEEP_TIME) { // No, SLEEP_TIME elapsed w/no switch?
    sleep();                           // Power down
    prev = t;                          // Reset timer on wake
  }
#endif

  if (autoCycle) {
    if ((t - lastImageTime) >= (CYCLE_TIME * 1000L)) nextImage();
    // CPU clocks vary slightly; multiple poi won't stay in perfect sync.
    // Keep this in mind when using auto-cycle mode, you may want to cull
    // the image selection to avoid unintentional regrettable combinations.
  }
#ifdef SELECT_PIN
  if (digitalRead(SELECT_PIN)) {       // Image select?
    debounce = 0;                      // Not pressed -- reset counter
  } else {                             // Pressed...
    if (++debounce >= 25) {            // Debounce input
      nextImage();                     // Switch to next image
      while (!digitalRead(SELECT_PIN)); // Wait for release
      // If held 1+ sec, toggle auto-cycle mode on/off
      if ((millis() - t) >= 1000L) autoCycle = !autoCycle;
      debounce = 0;
    }
  }
#endif

  // Transfer one scanline from pixel data to LED strip:
  if (!startedImage)
  {
    startedImageTime = millis();
    startedImage = true;
  }
  // If you're really pressed for graphics space and need just a few extra
  // scanlines, and know for a fact you won't be using certain image modes,
  // you can comment out the corresponding blocks below.  e.g. PALETTE8 and
  // TRUECOLOR are somewhat impractical on Trinket, and commenting them out
  // can free up nearly 200 bytes of extra image storage.

  switch (imageType) {

    case PALETTE1: { // 1-bit (2 color) palette-based image
        uint8_t  pixelNum = 0, byteNum, bitNum, pixels, idx,
                 *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS / 8];
        for (byteNum = NUM_LEDS / 8; byteNum--; ) { // Always padded to next byte
          pixels = pgm_read_byte(ptr++);  // 8 pixels of data (pixel 0 = LSB)
          for (bitNum = 8; bitNum--; pixels >>= 1) {
            idx = pixels & 1; // Color table index for pixel (0 or 1)
            strip.setPixelColor(pixelNum++,
                                palette[idx][0], palette[idx][1], palette[idx][2]);
          }
        }
        break;
      }

    case PALETTE4: { // 4-bit (16 color) palette-based image
        uint8_t  pixelNum, p1, p2,
                 *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS / 2];
        for (pixelNum = 0; pixelNum < NUM_LEDS; ) {
          p2  = pgm_read_byte(ptr++); // Data for two pixels...
          p1  = p2 >> 4;              // Shift down 4 bits for first pixel
          p2 &= 0x0F;                 // Mask out low 4 bits for second pixel
          strip.setPixelColor(pixelNum++,
                              palette[p1][0], palette[p1][1], palette[p1][2]);
          strip.setPixelColor(pixelNum++,
                              palette[p2][0], palette[p2][1], palette[p2][2]);
        }
        break;
      }

    case PALETTE8: { // 8-bit (256 color) PROGMEM-palette-based image
        uint16_t  o;
        uint8_t   pixelNum,
                  *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS];
        for (pixelNum = 0; pixelNum < NUM_LEDS; pixelNum++) {
          o = pgm_read_byte(ptr++) * 3; // Offset into imagePalette
          strip.setPixelColor(pixelNum,
                              pgm_read_byte(&imagePalette[o]),
                              pgm_read_byte(&imagePalette[o + 1]),
                              pgm_read_byte(&imagePalette[o + 2]));
        }
        break;
      }

    case TRUECOLOR: { // 24-bit ('truecolor') image (no palette)
        uint8_t  pixelNum, r, g, b,
                 *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS * 3];
        for (pixelNum = 0; pixelNum < NUM_LEDS; pixelNum++) {
          r = pgm_read_byte(ptr++);
          g = pgm_read_byte(ptr++);
          b = pgm_read_byte(ptr++);
          strip.setPixelColor(pixelNum, r, g, b);
        }
        break;
      }
  }

  strip.show(); // Refresh LEDs
#if !defined(LED_DATA_PIN) && !defined(LED_CLOCK_PIN)
  delayMicroseconds(900);  // Because hardware SPI is fast
#endif
    Serial.println(millis());
     // Check if all lines of the image have been displayed
    if (++imageLine >= imageLines) {
      // Record the time it took to display the entire image
      if (startedImage && !measuredImage) {
        finishedImage = millis();
        timeItTakesImage = finishedImage - startedImageTime; // Corrected calculation
        measuredImage = true;
        Serial.print("the time it takes to image: ");
        Serial.println(timeItTakesImage);
      }
      // Reset line counter for next image
      imageLine = 0;
    }


    if (timeItTakesImage > 0 && duration > 0) {
      // Toggle the LED pin to mark the end of the image display
      
      // Calculate the delay time while handling overflow
      uint32_t timeDifference = 0;
      if (duration >= timeItTakesImage) {
        timeDifference = duration - timeItTakesImage;
      }
   
      delaytime = timeDifference / imageLines;
    }
    if(delaytime>0){
      delay(delaytime);
    }
  }


// POWER-SAVING STUFF -- Relentlessly non-portable -------------------------

#ifdef MOTION_PIN
void sleep() {

  // Turn off LEDs...
  strip.clear();                 // Issue '0' data
  strip.show();
#ifdef POWER_PIN
  digitalWrite(POWER_PIN, HIGH); // Cut power
#if !defined(LED_DATA_PIN) && !defined(LED_CLOCK_PIN)
#ifdef __AVR_ATtiny85__
  pinMode(1, INPUT);             // Set SPI data & clock to inputs else
  pinMode(2, INPUT);             // DotStars power parasitically, jerks.
#else
  pinMode(11, INPUT);
  pinMode(13, INPUT);
#endif // ATtiny
#endif // Data/clock/pins
#endif // POWER_PIN

  power_all_disable(); // Peripherals ALL OFF, best sleep-state battery use

  // Enable pin-change interrupt on motion pin
#ifdef __AVR_ATtiny85__
  PCMSK = _BV(MOTION_PIN);  // Pin mask
  GIMSK = _BV(PCIE);        // Interrupt enable
#else
  volatile uint8_t *p = portInputRegister(digitalPinToPort(MOTION_PIN));
  if (p == &PIND) {         // Pins 0-7 = PCINT16-23
    PCMSK2 = _BV(MOTION_PIN);
    PCICR  = _BV(PCIE2);
  } else if (p == &PINB) {  // Pins 8-13 = PCINT0-5
    PCMSK0 = _BV(MOTION_PIN - 8);
    PCICR  = _BV(PCIE0);
  } else if (p == &PINC) {  // Pins 14-20 = PCINT8-14
    PCMSK1 = _BV(MOTION_PIN - 14);
    PCICR  = _BV(PCIE1);
  }
#endif

  // If select pin is enabled, that wakes too!
#ifdef SELECT_PIN
  debounce = 0;
#ifdef __AVR_ATtiny85__
  PCMSK |= _BV(SELECT_PIN); // Add'l pin mask
#else
  volatile uint8_t *p = portInputRegister(digitalPinToPort(SELECT_PIN));
  if (p == &PIND) {       // Pins 0-7 = PCINT16-23
    PCMSK2 = _BV(SELECT_PIN);
    PCICR  = _BV(PCIE2);
  } else if (p == &PINB) { // Pins 8-13 = PCINT0-5
    PCMSK0 = _BV(SELECT_PIN - 8);
    PCICR  = _BV(PCIE0);
  } else if (p == &PINC) { // Pins 14-20 = PCINT8-14
    PCMSK1 = _BV(SELECT_PIN - 14);
    PCICR  = _BV(PCIE1);
  }
#endif // ATtiny
#endif // SELECT_PIN
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Deepest sleep mode
  sleep_enable();
  interrupts();
  sleep_mode();                        // Power down

  // Resumes here on wake

  // Clear pin change settings so interrupt won't fire again
#ifdef __AVR_ATtiny85__
  GIMSK = PCMSK = 0;
#else
  PCICR = PCMSK0 = PCMSK1 = PCMSK2 = 0;
#endif
  power_timer0_enable();        // Used by millis()
#if !defined(LED_DATA_PIN) && !defined(LED_CLOCK_PIN)
#ifdef __AVR_ATtiny85__
  pinMode(1, OUTPUT);           // Re-enable SPI pins
  pinMode(2, OUTPUT);
  power_usi_enable();           // Used by DotStar
#else
  pinMode(11, OUTPUT);          // Re-enable SPI pins
  pinMode(13, OUTPUT);
  power_spi_enable();           // Used by DotStar
#endif // ATtiny
#endif // Data/clock pins
#ifdef POWER_PIN
  digitalWrite(POWER_PIN, LOW); // Power-up LEDs
#endif
  prev = millis();              // Save wake time
}

EMPTY_INTERRUPT(PCINT0_vect); // Pin change (does nothing, but required)
#ifndef __AVR_ATtiny85__
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));
#endif

#endif // MOTION_PIN

// Battery monitoring idea adapted from JeeLabs article:
// jeelabs.org/2012/05/04/measuring-vcc-via-the-bandgap/
// Code from Adafruit TimeSquare project, added Trinket support.
// In a pinch, the poi code can work on a 3V Trinket, but the battery
// monitor will not work correctly (due to the 3.3V regulator), so
// maybe just comment out any reference to this code in that case.
uint16_t readVoltage() {
  int      i, prev;
  uint8_t  count;
  uint16_t mV;

  // Select AVcc voltage reference + Bandgap (1.8V) input
#ifdef __AVR_ATtiny85__
  ADMUX  = _BV(MUX3) | _BV(MUX2);
#else
  ADMUX  = _BV(REFS0) |
           _BV(MUX3)  | _BV(MUX2) | _BV(MUX1);
#endif
  ADCSRA = _BV(ADEN)  |                          // Enable ADC
           _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 1/128 prescaler (125 KHz)
  // Datasheet notes that the first bandgap reading is usually garbage as
  // voltages are stabilizing.  It practice, it seems to take a bit longer
  // than that.  Tried various delays, but still inconsistent and kludgey.
  // Instead, repeated readings are taken until four concurrent readings
  // stabilize within 10 mV.
  for (prev = 9999, count = 0; count < 4; ) {
    for (ADCSRA |= _BV(ADSC); ADCSRA & _BV(ADSC); ); // Start, await ADC conv.
    i  = ADC;                                       // Result
    mV = i ? (1100L * 1023 / i) : 0;                // Scale to millivolts
    if (abs((int)mV - prev) <= 10) count++;  // +1 stable reading
    else                          count = 0; // too much change, start over
    prev = mV;
  }
  ADCSRA = 0; // ADC off
  return mV;
}```

type or paste code here

So... You are following that project but you have made some changes to the code from the project? Can you describe what those changes were?

so i will go in detail with the changes i made:
0. defining new pins for hall sensor:

#define LED_DATA_PIN  11
#define LED_CLOCK_PIN 13
#define HALL_SENSOR_PIN 3
#define LED_HALLPPIN 12
  1. new varliables:
boolean startedImage = false; // Flag indicating if an image has started
boolean measuredImage = false; // Flag indicating if an image has been measured
uint32_t startedImageTime, finishedImage, duration, startTime, timeItTakesImage, delaytime = 0;
  1. implementing the hall sensor with interrupt:
void isr() {
  uint32_t now = millis();
  duration = now - startTime;
  startTime = now;
  ledState = !ledState; // Toggle LED state
  digitalWrite(LED_HALLPPIN, ledState);

}

and from the setup function:

  pinMode(HALL_SENSOR_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(HALL_SENSOR_PIN), isr, FALLING);

3.in the loop function a logic that measures how long it takes to show an image, two parts:
first:

  if (!startedImage)
  {
    startedImageTime = millis();
    startedImage = true;
  }

and second:

     // Check if all lines of the image have been displayed
    if (++imageLine >= imageLines) {
      // Record the time it took to display the entire image
      if (startedImage && !measuredImage) {
        finishedImage = millis();
        timeItTakesImage = finishedImage - startedImageTime; // Corrected calculation
        measuredImage = true;
        Serial.print("the time it takes to image: ");
        Serial.println(timeItTakesImage);
      }
      // Reset line counter for next image
      imageLine = 0;
    }
  1. and this is the major one, implementing a delay that will spread the flickering through the all spin:

    if (timeItTakesImage > 0 && duration > 0) {
      // Toggle the LED pin to mark the end of the image display
      
      // Calculate the delay time while handling overflow
      uint32_t timeDifference = 0;
      if (duration >= timeItTakesImage) {
        timeDifference = duration - timeItTakesImage;
      }
   
      delaytime = timeDifference / imageLines;
    }
    if(delaytime>0){
      delay(delaytime);
    }
  }

343 ms/revolution for a bike wheel ~0.82 m in diameter.

Thank you.. I ride a road bike, Merida scultura 400
In which the diameter is around 62-58cm
So how that calculation would go?

so i found this on quora:
"a typical wheel (700c) is about 82″ inches circumference, a mile i 5,280 feet x 12 =63360 inches so at 20 mph (a typical somewhat fast speed) you travel 1,267,200 inches in an hour dividing by 60 gives you 21,270 inches per minute, dividing that by the 82 inches circumference gives you 257.56 RPM. With a smaller wheel such as a 26″ it would be a little more. Your question was quite reasonable and any intelligent person who hasn’t spent to much time watching TV would have no trouble answering it!"

and that was kind of disappointing as od 258 rpm means 4.3 rps
and the whole image is a second.. this is really doesnt add up as there were examples of very complicated images in examples i found (without code) like:

and

another update which i think is important is that to reduce the "loop" time, i transformed this logic:

    if (timeItTakesImage > 0 && duration > 0) {
      // Toggle the LED pin to mark the end of the image display
      
      // Calculate the delay time while handling overflow
      uint32_t timeDifference = 0;
      if (duration >= timeItTakesImage) {
        timeDifference = duration - timeItTakesImage;
      }
   
      delaytime = timeDifference / imageLines;
    }

to the interrupt function as there is no need to calculate again if the magnet didn't pass, didn't save a lot of time in the loop function, still takes around a second to do the full image without delay

Since the wheel doesn't slip, in one wheel revolution the bike moves forward by one wheel circumference C = Pi x D.

At 7.5 m/s, the time to travel one wheel revolution with D = 0.62 m is C/(7.5 m/s) = 3.142*0.62/7.5 = 260 ms

the time it takes to "show" an image that is 100 pixel wide is 1 second!

Probably not.

You haven't described the problem with the code well enough for me to understand it, but I don't see how it can work well. There are several problems with the code.

For one, variables shared with interrupt routines must be declared "volatile", and the critical ones duration and startTime are not.

void isr() {
  uint32_t now = millis();
  duration = now - startTime;
  startTime = now;
  ledState = !ledState; // Toggle LED state
  digitalWrite(LED_HALLPPIN, ledState);
}

This isn't always fatal, and it may have worked in the Adafruit demo, but it is an obvious problem.

Furthermore, accessing variables shared with interrupt routines must be protected during access, otherwise they can be corrupted by the interrupt while the main loop is accessing them.

The fix for that is to make a copy with the interrupts turned off, and use the copy. e.g. in the main program:

volatile uint32_t duration;   //global variable

// in loop() ...

noInterrupts();
uint32_t duration_copy = duration;
interrupts();
Serial.println(duration_copy); 
1 Like

Okay, will implement that and update.
I think the main problem, and sorry if it wasn’t clear is it that the “timeittakesimage” variable seems now way too long.. like something with hardware or software doesn’t blink the image fast enough..

The Instructables author says:

I would recommend you to use the Adafruit guide.

I agree, at least while you are getting started.

They don't use an interrupt routine for anything except waking up the processor, so their code does not have your problem with shared variables.

1 Like

Will do again.. I will mention I am four months already on this, and texting with an Adafruit expert that is trying to help me, the interrupt function is something he saw and didn’t mention your notes, but I am here to learn, going to sit now on the computer and try.. maybe download the code they wrote and just trying to measure how long it takes to display an image on their code
I will add a link to the forum in adafruit where you can see additional information if interested and also for those who will try to do this after me

https://forums.adafruit.com/viewtopic.php?t=208415&sid=9172eeae5da7a6b9fa4f6cf8fc951121&start=15

amazing
downloading there code and it takes 186 millis..
now lets work.. the problem is a code problem
after implementing your suggestion seems to be also in appropriate time frame in my code too also around 186 millis
now i will debug the hall sensor and the interactivity
|and then on to the bike :slight_smile:
looks good enough to come to the bike again and be smarter after
will update next week
the code looks good after changing variables as you said!
so thanks!

Great! When you are happy with the result, please post it on the forum, as others will be interested.

Hi again, so I assembled it on the bike and still it is not visible.. what would be a good graphic to test this mechanism? Maybe my graphic is too much or too little.. do we need to calculate pixels as cm and try to achieve a full circle of image?

Adding a video of my latest attempt:

Thanks!
Maybe I need to move to an esp32?
Is that the same code just a different processor? Is he more suitable?

my current graphic attempt is added by file:
movingon

sorry for editing a lot of times i just wanted to add, the code seams to be working fine, and i will explain:
when the sensor goes to the magnet he detects it , and you can see it slowing down or speeding up accordingly to the wheel spin (when i span it manually by hand, maybe in the video it is less visible)
so maybe the problem is with the processor speed, maybe the led speed, and maybe even the graphic.. but software wise i think we are good.. but maybe i am wrong

Another edit:
I read the instructables again and she wrote something about around 84 pixels worked for her well, my image is around 130 pixels, will change and try and update.. meanwhile any more suggestions will help

another edit:
in the same instructables there seems to be a gif example that we can also see the output in the images added to the tutorial , in the git hub, the text seems to be upside down, does that have any logic? is that may be the problem?

but in the adafruit tutorial they appear just right:

my game plan:

  1. use the examples (gifs) as in the github from the instructables
  2. if that fails, change the code to the code in the git hub
  3. if that fails, change the microcontroller to an esp32
    (maybe 2+3 are the same)
  4. if that fails call quits on my "own" way and use the same materials as in the instructables one to one

so i followed the:

but with minor changes:

  1. i used esp32
  2. i decided to use the pull up inside the pin 2 of esp32 instead of a external resistor
  3. i did switched to a reed switch instead of an hall sensor (like the instructables suggests)
  4. i did not use the capacitor but will do later this week

still not quite good results.. going to try to use esp32 wi fi functionality to control and find the sweet spot from afar, and then learn how it can do it by itself..

so after another huge disappointment as even though wifi adjustable
and making the delay time to be as fast as possible i still wasn't able to get a good result
my question is, if dividing by 4 will help?
will it make it more clear what is visible?
if so why?
because they have the same speed even slower (data will have to be transfred to 4 led strip instead of one in spi control)
going to try this:

hi so after modeling 3d printing and testing i came to my final try and obstacle
my wiring is as follows:(ESP32 to Logic Level Shifter)
pin 26 -- LV1 - HV1 - CLOCK
pin 27 -- LV2 - HV2 - DATA
3.3V - LV
GND - GND
pin 22 - LV3 - HV3
pin 21 - LV4 - HV4
Vin - HV (power supply powering the esp32 is a usb cable connected to the computer and in the future power bank)
GND(the one next to Vin) - GND of the HV one

from Logic Level Shifter to MPU6050 -
HV4 SCL
HV3 SDA
Vin - Vcc
Gnd - Gnd

from logic level to led strip apa102
HV1 - CLOCK in Led Strip
HV2 - DATA in Led Strip
Vin - 5v in led strip
Gnd - Gnd

before putting in on bike i used this example and saw voltage in the voltmeter all throgh the 128 strip
in the code you can see i tried to make only one worked but that wasnt succeful
looking for help

// Simple strand test for Adafruit Dot Star RGB LED strip.
// This is a basic diagnostic tool, NOT a graphics demo...helps confirm
// correct wiring and tests each pixel's ability to display red, green
// and blue and to forward data down the line.  By limiting the number
// and color of LEDs, it's reasonably safe to power a couple meters off
// the Arduino's 5V pin.  DON'T try that with other code!

#include <Adafruit_DotStar.h>
// Because conditional #includes don't work w/Arduino sketches...
#include <SPI.h>         // COMMENT OUT THIS LINE FOR GEMMA OR TRINKET
//#include <avr/power.h> // ENABLE THIS LINE FOR GEMMA OR TRINKET

#define NUMPIXELS 1 // Number of LEDs in strip

// Here's how to control the LEDs from any two pins:
#define DATAPIN    27
#define CLOCKPIN   26
Adafruit_DotStar strip(NUMPIXELS, DATAPIN, CLOCKPIN, DOTSTAR_BRG);
// The last parameter is optional -- this is the color data order of the
// DotStar strip, which has changed over time in different production runs.
// Your code just uses R,G,B colors, the library then reassigns as needed.
// Default is DOTSTAR_BRG, so change this if you have an earlier strip.

// Hardware SPI is a little faster, but must be wired to specific pins
// (Arduino Uno = pin 11 for data, 13 for clock, other boards are different).
//Adafruit_DotStar strip(NUMPIXELS, DOTSTAR_BRG);

void setup() {

#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000L)
  clock_prescale_set(clock_div_1); // Enable 16 MHz on Trinket
#endif

  strip.begin(); // Initialize pins for output
  strip.show();  // Turn all LEDs off ASAP
}

// Runs 10 LEDs at a time along strip, cycling through red, green and blue.
// This requires about 200 mA for all the 'on' pixels + 1 mA per 'off' pixel.

int      head  = 0, tail = -10; // Index of first 'on' and 'off' pixels
uint32_t color = 0xFF0000;      // 'On' color (starts red)

void loop() {

  strip.setPixelColor(head, color); // 'On' pixel at head
  strip.setPixelColor(tail, 0);     // 'Off' pixel at tail
  strip.show();                     // Refresh strip
  delay(20);                        // Pause 20 milliseconds (~50 FPS)

  if(++head >= NUMPIXELS) {         // Increment head index.  Off end of strip?
    head = 0;                       //  Yes, reset head index to start
    if((color >>= 8) == 0)          //  Next color (R->G->B) ... past blue now?
      color = 0xFF0000;             //   Yes, reset to red
  }
  if(++tail >= NUMPIXELS) tail = 0; // Increment, reset tail index
}

622mm exactly to ISO. All other numbers are proprietary.

changin the pins to 18-19 helped
and also changing the led strips and the microcontroller itself.. happy to have spares
will fix and inspect after the show is all done
gonna test on Thursday on bike

Add counterweights.

So I assembled everything and it worked only on slow speeds and then not at all. What I mean by that is that when I connected it again to the power bank even the red led was weak and the led didn’t even turn on. Back to the sketching table and thinking..
can’t change my esp32 every time, maybe the power supply is killing him.. maybe the power consumption is too high.
Mpu6050 and 128 apa102 leds
Maybe other option I don’t know of..
any way the show will happen without it because I present next week.
Maybe it will feature in the next one.

There is another project/discussion on the forum where power banks are not working. Use an ebike battery. They are made for trickle (LCD display) and high current (250W and up).

Will try. Tried also the good old usb port and computer and that didn’t work either, thinking of switching to the raspberry pi so that he could handle those fast calculations and fast speeds..
but maybe I am wrong and it is wiring issues or code issues that make the microcontroller faint.