P.O.V. for bicycle (en)

Logo de Creative Common License    Laurent Brunel. Creative Common License CC-BY: Knowledge is open if anyone is free to access, use, modify, and share it — subject, at most, to measures that preserve provenance and openness.

This projet use the Persitence Of Vision property of our eyes to display things with the wheel of a bicycle. The device is installed in the bike in one second by inserting it between the spokes: the centrifugal force does the rest… We decided to display the bike speed and some characters changeable with the mobile phone.
We 3D printed a body design with FreeCAD to carry an Arduino micro pro connected to a Bluetooth module HC05 (Mixtraltech).

Detalle constructivo Pov bicicleta

dibujo-estructura-pov-fablab-santander

This POV is equipped with 8 bright LEDs (Cree C566C-AFN-CU0W0251 Amber LED, 596 nm RS 810-7235) connected directly to the arduino pins (with a resistor) and a IR sensor (ref LTH-1550-01) able to detect when the POV pass in front of a white target pasted in the frame.

detalle-del-fotodiodo-y-led-de-paso-de-un-pov-de-bicicleta

A 1400mAh Li-po battery is placed for energy. The IR sensor permits to measure the time taken
by a wheel turn, to calculate the bike speed knowing the wheel diameter and display it.

The bluetooth allows a simple APP for android made with AppInventor to send by serial a new word to display beside the speed value.
Arduino code, STL 3D model of the body and FreeCAD source file here

    Inspired by shrimpov.

————————————————————————————————————————————-

arduino code:

————————————————————————————————————————————–

#define maxMessageLenght 50 //max nb of char in message before CR and LF

//inspired by shrimpov: https://github.com/embecosm/shrimp-pov/blob/master/code/shrimPov/shrimPov.ino
// Font: commodore_64_pixelised
// 528 bytes
byte fontHeight = 8;
int fontOffsets[] = {
/*EXCLAMATION*/ 16, /*”*/ 48, /*#*/ 64, /*$*/ /*48*/ 64, /*%*/ 48, /*&*/ 56, /*’*/ 32, /*(*/ 32, /*)*/ 32, /***/ 64,
/*+*/ 48, /*,*/ 24, /*-*/ 48, /*STOP*/ 16, /*FWDSLASH*/ 56, /*0*/ 48, /*1*/ 48, /*2*/ 48, /*3*/ 48, /*4*/ 56, /*5*/ 48,
/*6*/ 48, /*7*/ 48, /*8*/ 48, /*9*/ 48, /*:*/ 16, /*;*/ 24, /*<*/ 48, /*=*/ 48, /*>*/ 48, /*QUESTION*/ 48, /*@*/ 48,
/*A*/ 48, /*B*/ 48, /*C*/ 48, /*D*/ 48, /*E*/ 48, /*F*/ 48, /*G*/ 48, /*H*/ 48, /*I*/ 32, /*J*/ 48, /*K*/ 48, /*L*/ 48,
/*M*/ 56, /*N*/ 48, /*O*/ 48, /*P*/ 48, /*Q*/ 48, /*R*/ 48, /*S*/ 48, /*T*/ 48, /*U*/ 48, /*V*/ 48, /*W*/ 56, /*X*/ 48,
/*Y*/ 48, /*Z*/ 48, /*[*/ 32, /*BACKSLASH*/ 56, /*]*/ 32, /*^*/ 48, /*_*/ 64, /*FWDAPOS*/ 32, /*a*/ 48, /*b*/ 48, /*c*/ 40,
/*d*/ 48, /*e*/ 48, /*f*/ 40, /*g*/ 48, /*h*/ 48, /*i*/ 32, /*j*/ 32, /*k*/ 48, /*l*/ 32, /*m*/ 56, /*n*/ 48, /*o*/ 48,
/*p*/ 48, /*q*/ 48, /*r*/ 48, /*s*/ 48, /*t*/ 48, /*u*/ 48, /*v*/ 48, /*w*/ 56, /*x*/ 48, /*y*/ 48, /*z*/ 48, /*{*/ 48,
/*|*/ 16, /*}*/ 48, 0
};

byte fontBytes[] = {
/*EXCLAMATION*/ 95, 95,
/*”*/ 3, 3, 0, 0, 3, 3,
/*#*/ 20, 127, 127, 20, 20, 127, 127, 20,
///*$*/ 36, 46, 107, 107, 58, 18,
/*$ [smilie] */ 60, 108, 219, 223, 223, 219, 108, 60,
/*%*/ 99, 51, 24, 12, 102, 99,
/*&*/ 50, 127, 77, 77, 119, 114, 80,
/*’*/ 4, 6, 3, 1,
/*(*/ 28, 62, 99, 65,
/*)*/ 65, 99, 62, 28,
/***/ 8, 42, 62, 28, 28, 62, 42, 8,
/*+*/ 8, 8, 62, 62, 8, 8,
/*,*/ 128, 224, 96,
/*-*/ 8, 8, 8, 8, 8, 8,
/*STOP*/ 96, 96,
/*FWDSLASH*/ 64, 96, 48, 24, 12, 6, 2,
/*0*/ 62, 127, 73, 69, 127, 62,
/*1*/ 64, 68, 127, 127, 64, 64,
/*2*/ 98, 115, 81, 73, 79, 70,
/*3*/ 34, 99, 73, 73, 127, 54,
/*4*/ 24, 24, 20, 22, 127, 127, 16,
/*5*/ 39, 103, 69, 69, 125, 57,
/*6*/ 62, 127, 73, 73, 123, 50,
/*7*/ 3, 3, 121, 125, 7, 3,
/*8*/ 54, 127, 73, 73, 127, 54,
/*9*/ 38, 111, 73, 73, 127, 62,
/*:*/ 99, 99,
/*;*/ 128, 227, 99,
/*<*/ 8, 28, 54, 99, 65, 65,
/*=*/ 20, 20, 20, 20, 20, 20,
/*>*/ 65, 65, 99, 54, 28, 8,
/*QUESTION*/ 2, 3, 81, 89, 15, 6,
/*@*/ 62, 127, 65, 77, 79, 46,
/*A*/ 124, 126, 11, 11, 126, 124,
/*B*/ 127, 127, 73, 73, 127, 54,
/*C*/ 62, 127, 65, 65, 99, 34,
/*D*/ 127, 127, 65, 99, 62, 28,
/*E*/ 127, 127, 73, 73, 65, 65,
/*F*/ 127, 127, 9, 9, 1, 1,
/*G*/ 62, 127, 65, 73, 123, 58,
/*H*/ 127, 127, 8, 8, 127, 127,
/*I*/ 65, 127, 127, 65,
/*J*/ 32, 96, 65, 127, 63, 1,
/*K*/ 127, 127, 28, 54, 99, 65,
/*L*/ 127, 127, 64, 64, 64, 64,
/*M*/ 127, 127, 6, 12, 6, 127, 127,
/*N*/ 127, 127, 14, 28, 127, 127,
/*O*/ 62, 127, 65, 65, 127, 62,
/*P*/ 127, 127, 9, 9, 15, 6,
/*Q*/ 30, 63, 33, 97, 127, 94,
/*R*/ 127, 127, 25, 57, 111, 70,
/*S*/ 38, 111, 73, 73, 123, 50,
/*T*/ 1, 1, 127, 127, 1, 1,
/*U*/ 63, 127, 64, 64, 127, 63,
/*V*/ 31, 63, 96, 96, 63, 31,
/*W*/ 127, 127, 48, 24, 48, 127, 127,
/*X*/ 99, 119, 28, 28, 119, 99,
/*Y*/ 7, 15, 120, 120, 15, 7,
/*Z*/ 97, 113, 89, 77, 71, 67,
/*[*/ 127, 127, 65, 65,
/*BACKSLASH*/ 2, 6, 12, 24, 48, 96, 64,
/*]*/ 65, 65, 127, 127,
/*^*/ 4, 6, 127, 127, 6, 4,
/*_*/ 64, 64, 64, 64, 64, 64, 64, 64,
/*FWDAPOS*/ 1, 3, 6, 4,
/*a*/ 32, 116, 84, 84, 124, 120,
/*b*/ 126, 126, 72, 72, 120, 48,
/*c*/ 56, 124, 68, 68, 68,
/*d*/ 48, 120, 72, 72, 126, 126,
/*e*/ 56, 124, 84, 84, 92, 24,
/*f*/ 8, 124, 126, 10, 10,
/*g*/ 152, 188, 164, 164, 252, 124,
/*h*/ 127, 127, 4, 4, 124, 120,
/*i*/ 68, 125, 125, 64,
/*j*/ 128, 128, 250, 122,
/*k*/ 127, 127, 16, 56, 104, 64,
/*l*/ 65, 127, 127, 64,
/*m*/ 124, 124, 24, 56, 28, 124, 120,
/*n*/ 124, 124, 4, 4, 124, 120,
/*o*/ 56, 124, 68, 68, 124, 56,
/*p*/ 252, 252, 36, 36, 60, 24,
/*q*/ 24, 60, 36, 36, 252, 252,
/*r*/ 124, 124, 4, 4, 12, 8,
/*s*/ 72, 92, 84, 84, 116, 36,
/*t*/ 4, 4, 62, 126, 68, 68,
/*u*/ 60, 124, 64, 64, 124, 124,
/*v*/ 28, 60, 96, 96, 60, 28,
/*w*/ 28, 124, 112, 56, 112, 124, 28,
/*x*/ 68, 108, 56, 56, 108, 68,
/*y*/ 156, 188, 160, 224, 124, 60,
/*z*/ 68, 100, 116, 92, 76, 68,
/*{*/ 8, 8, 62, 119, 65, 65,
/*|*/ 127, 127,
/*}*/ 65, 65, 119, 62, 8, 8, 0
};

int numSides = 4; //the number of sides in the tilt switch
int tiltPins[] = {8,7,5,6}; //listed in clockwise direction
int numLights = 8;

// pins for the pov bici
int povPins[] = {9,8,7,6,5,4,3,2};
int pinSensor=10;//sensor to measure period (or angular speed)
//pins for the test on the 16leds pov:
//int povPins[] = {22,24,26,28,30,32,34,36};
//int pinSensor=2;//sensor to measure period (or angular speed)

int columnDelay = 1;
int spaceCols = 4;

//String message = ” 0 “;
char message[maxMessageLenght];
int messageLength=1;

int lastState=0;
unsigned long time0,time1,period;

void drawMessage(){

for(int letterPos = 0; letterPos < messageLength; letterPos++){

//use ascii charcode to index font information starting with 33 = first printable char
int fontPos = int(message[letterPos]) – 33;

//draw space or letter
if(fontPos == -1){
//it’s a space
drawSpace();
}
else{
//it’s a printable character. draw it
drawCharacter(fontPos);
}

//draw a space between each character of message
drawBlankCol();
}
}
void drawMess(char* mess,int messLength)
{

for(int letterPos = 0; letterPos < messLength; letterPos++){

//use ascii charcode to index font information starting with 33 = first printable char
int fontPos = int(mess[letterPos]) – 33;

//draw space or letter
if(fontPos == -1){
//it’s a space
drawSpace();
}
else{
//it’s a printable character. draw it
drawCharacter(fontPos);
}

//draw a space between each character of message
drawBlankCol();
}
}

void drawBlankCol(){
for(int row = 0; row < fontHeight; row++){
digitalWrite(povPins[row],LOW);
}
delay(columnDelay);
}

void drawSpace(){
for(int col = 0; col < spaceCols; col++){
drawBlankCol();
}
}

void drawCharacter(int fontPos)
{
int startBit = fontOffsets[fontPos]; //first bit of character
int endBit = fontOffsets[fontPos + 1]; //start of next char (or dummy char at end)

for(int colStart = startBit; colStart < endBit; colStart += fontHeight)
{
for(int row = 0; row < fontHeight; row++)
{
//calculate the bit position
int bitPos = colStart + row;
int byteOffset = bitPos / 8;
int bitOffset = bitPos % 8;
//read the individual bit
if(((fontBytes[byteOffset]) & (1 << bitOffset)) == 0)
{
digitalWrite(povPins[row], LOW);
}
else
{
digitalWrite(povPins[row], HIGH);
}
}
delay(columnDelay); //wait between columns
}
}
/** Configure input and output pins corresponding with each tilt direction to begin. */
void setup()
{
Serial.begin(9600);

message[0]=’F’;
message[1]=’A’;
message[2]=’B’;
message[3]=’_’;
message[4]=’L’;
message[5]=’A’;
message[6]=’B’;
messageLength=7;

for(int side = 0; side < numSides; side++){
//configure tilt pins as input and set high resistance
pinMode(tiltPins[side],INPUT);
digitalWrite(tiltPins[side],LOW);
}

for(int light = 0; light < numLights; light++){
//configure lights as output and turn off
pinMode(povPins[light], OUTPUT);
digitalWrite(povPins[light],LOW);
}

//translate offsets from relative (character width) into absolute (cumulative width so far)
int numOffsets = sizeof(fontOffsets) / sizeof(int);
int bitCount = 0;
for(int offsetIdx = 0; offsetIdx < numOffsets; offsetIdx++){
int charWidth = fontOffsets[offsetIdx];
fontOffsets[offsetIdx] = bitCount;
bitCount += charWidth;
}

pinMode(pinSensor, INPUT);

}

/** visit each side in turn to check if it’s connected
* and display each direction using different LEDs
*/
void loop()
{

int i;
char messPeriod[9];
if (Serial.available())
{
readMessage();
//Serial.println(message);
}

if (digitalRead(pinSensor) == HIGH) // read the state of the speed sensor
{
// turn LED on:
//digitalWrite(13, HIGH);
if (lastState==LOW)
{
time0=millis();
period=time0-time1;
if (period>800)period=800;
//delay(100);
time1=time0;

//wait half turn
delay(period*0.4);

//print the speed:
int v100=(int)(735100/period);
int i2=v100/100;
int i1=(v100-i2*100)/10;
int i0=v100-i2*100-i1*10;
messPeriod[0]=i2+48;
messPeriod[1]=’.’;
messPeriod[2]=i1+48;
messPeriod[3]=i0+48;
messPeriod[4]=’ ‘;
messPeriod[5]=’k’;
messPeriod[6]=’m’;
messPeriod[7]=’/’;
messPeriod[8]=’h’;
drawMess(messPeriod,9);

drawSpace();
drawSpace();

//for (int i=0;i<5;i++)
{
drawMessage();
//if ((millis()-time0)>(period*0.8)) break;
//drawSpace();
//drawSpace();
}
}
lastState=HIGH;
}
else
{
// turn LED off:
// digitalWrite(13, LOW);
lastState=LOW;
}

}
void readMessage()
{
int i=0;
for(;;)
{
char incomingByte = Serial.read();
delay(2);
if (incomingByte==-1) break;
if (incomingByte==’;’) break;//until ;
if (incomingByte==10) break;//until New Line;
if (incomingByte==13) break;//until Carriage Return;
if (i==maxMessageLenght-1) //Message too long
{
break;
}
message[i]=incomingByte;
i++;
}

messageLength=i;
if (messageLength>10) messageLength=10;
/*
Serial.print(“messageLenght “);
Serial.println(messageLenght, DEC);
Serial.print(“*”);
for (int i=0;i<messageLenght;i++) Serial.print(message[i]);
Serial.print(“*”);
Serial.println();
*/
}
/** Keeps a value within bounds when overflowing or underflowing,
* so that incrementing or decrementing a number stays in bounds.
* (workaround for the fact that % in C isn’t a mathematical modulus and behaves weird with negative numbers) */
int wrap(int value, int upperBound){
return value < 0 ? ((value % upperBound) + upperBound) % upperBound : value % upperBound;
}

Los comentarios están cerrados.