Squirrel Wheel Speed ​​Counter

I got a regular analog squirrel in my house.
She lived in a cage, she lived for herself, she turned her wheel. At different speeds. In the morning, usually more energetically, in the evening, tired, more slowly. And all this time one question haunted me. And at what speed does the squirrel run in the wheel?
I thought for a long time and once ...
I decided to make a wheel speed counter .

In the photo is a prototype. Therefore, not everything is done neatly.

image

Sortsa, video and final results of measurements under a cat.


Components.


  1. Arduino Uno.
  2. Screen LKM-1638.
  3. Hall sensor (anyone will do).
  4. Expansion board.
  5. Bonus - Temperature and humidity sensor DHT11.


In principle, all I needed was a microcontroller on the board, an LKM-1638 screen and a simple Hall sensor, but, choosing all this, I also took a DHT11 humidity and temperature sensor to pump the squirrel cage right away to “smart”! Ordered on dx.com.

I did everything myself, with my own hands.

I chose the ready-made Arduino Uno board, so as not to waste time on wiring, etching, programmers, etc., but immediately proceed with the implementation of the plan. The screen took the LKM-1638, because it is bright, crisp , with large and understandable numbers.

Along the way, buttons on the display came in handy for displaying statistical information.

Pressing the corresponding button displays:


  1. Total mileage.
  2. Maximum lap speed.
  3. The average daily mileage in kilometers.
  4. Average speed per lap.
  5. UpTime in days
  6. The total number of revolutions of the circle
  7. Current humidity.
  8. Current temperature.


It turned out almost like in Formula 1. If there were several squirrels, you could arrange your blackjack tote.

Yes, and even there under the buttons on the display there were LEDs in an amount equal to the number of buttons. With their help, it was decided to visualize the speed. Make it look like a digital tachometer. According to the principle: the more diodes are lit, the closer the current speed is to the maximum, the lower, the corresponding to the minimum.

I put it all together, connected the wires and started writing code.
I took the library for the screen ready here .
For temperature and humidity sensors, examples and a library are here .
For the Hall sensor, I took everything from my head.

The logic of the speed counter is very simple.
The Hall sensor is attached to the cage at the point closest to the wheel. On the wheel is a small but very strong neodymium magnet that I pulled out of the head of an old DVD drive.
When the wheel rotates, with each whole revolution, the magnet briefly changes the magnetic field in the radius of measurement of the Hall sensor, changing in the latter state from 1 to 0. If the sensor was in the opposite position, the value would change from 0 to 1 (which is probably more correct ), but then the sensor would stay worse on the cell.
The time difference between the two occurrences of the magnet in the sensor coverage area is recognized as a whole circle. Further, knowing the inner circumference of the wheel, you can easily calculate the speed of the circle and display it on the display, which I did.
It should be noted here that this measurement method hasnuances :
  1. Only a full wheel revolution is considered. If a protein has scrolled, for example, 0.9 turns and jumped out of it, then this turn is not taken into account at all. Or if she turned the wheel 1.9, 2.9, 3.9 ... n.9 revolutions and repeated it many times in the process of testing the device, then a big error comes up in calculating the total mileage, and the speed of these incomplete circles is not taken into account in calculating the average speed.
  2. When the squirrel jumps out of the still spinning wheel in the direction opposite to the rotation, then by the counter-current, if the magnet at that time just passed the sensor, the magnet passes by the sensor once again in the opposite direction, erroneously recording a whole complete revolution of the wheel.

Measurement errors in these cases can be minimized by increasing the number of sensors, for example, to 4 and placing
them on opposite sides of the circle (through? / 2 radians or 90 degrees). Then you can take into account not only every quarter of a turn, which will definitely increase the accuracy of measurements, but also know the direction of rotation of the wheel (i.e., where the squirrel runs — home or from home to the neighbor).

The current wheel speed displayed in real time on the display in kilometers / hour.

Next is the code itself. I note that the code is quite working, but the version is not final. In the presence of time, I will still optimize it.

Whole code
/ *
sketch for calculating wheel revolutions
and displaying values ​​on an 8-digit screen
plus outputting the current temperature and humidity
release
* /

/ ************** For a digital sensor DHT11 ****** ********** /
#define dht_dpin A2

byte bGlobalErr;

byte dht_dat [5];
/ ************** End for digital sensor DHT11 ********** /

/ ******* variables for digital mode ******* ********** /

// library for the 8-segment digital module
#include "TM1638.h"
// define the pins in the data pin 5, clock pin 4 and strobe pin 6
TM1638 module (5, 4 , 6);
#define Red TM1638_COLOR_RED
#define Green TM1638_COLOR_GREEN
#define Red_green TM1638_COLOR_GREEN + TM1638_COLOR_RED

/ ************ end of variables for module *************** /

/ ********* variables for Hall sensor ******************** /

// wheel circumference in meters (to be specified)
static float One_round = 0.91;

// determine the port for connecting the Hall sensor
#define Hall_port 2
#define Max_led_light_speed 8
// interval of 10 seconds, if the wheel is considered to be only stopped longer
// if less, it is considered to be cool
#define Wait_interval 10000
// interval after which it turns off
#define screen Do_sleeping_interval 60000
// pause in sensor polls in normal mode
#define Delay_in_work 5
// pause in sensor polls in sleep mode
#define Delay_in_sleep 50

// counter of wheel revolutions
unsigned long Round_counter = 0;
// here we store the previous time for speed calculation
unsigned long Prev_time = 0;
// here we store the previous time for constant monitoring
unsigned long Prev_time_mon = 0;
// here is the data for monitoring pauses for zeroing and turning off the screen, etc.
unsigned long time_span_mon = 0;
// previous sensor value
byte PrevValue = 1;
// current wheel speed cm / c
double Curr_speed = 0;
// maximum wheel speed m / c
double Max_speed = 0;
// total mileage in meters for counting by button presses
double Total_run = 0;
// put the speeds of all circles here to calculate the average
double All_speeds = 0;
// how many days have passed
double Day_passed = 0;
// variable for storing the state of the system
// 0- the system in the energy saving and standby mode - the wheel has not been cool for a long time - the screen is off
// 1- the wheel has just stopped cool
// 2- the wheel of coolitz
byte Sys_status = 0;

/ *************** end of variables for the Hall ******************* /

void setup () {

Serial.begin (9600) ;
InitDHT (); // initialization DHT11 sensor
Serial.println ("Run!");

// turn on the display, displaying 0 on it
module.setDisplayToString ("0.", 0, 7);
delay (1000);

}

void loop () {

if (Sys_status == 0)
{
// if sleep mode, less often we interrogate the
delay sensor (Delay_in_sleep);
}
else
{
// if the operating mode, then more often we interrogate the
delay sensor (Delay_in_work);
}
/ ************** beginning of the buttons ******************************** ******** /

// listen to buttons
byte keys = module.getButtons ();

// if the button is pressed and the time is longer than the timeout, then turn on the display
if (keys! = 0 && Sys_status == 0)
{
module.setupDisplay (true, 7);
}

switch (keys) {
// if the leftmost
button is pressed case 0b00000001:

// blink with the appropriate diode, the rest is extinguished
module.setLEDs (0);
module.setLED (Red, 0);
module.clearDisplay ();
// display the total mileage in kilometers
Total_run = Round_counter * (double) One_round / 1000.00;

DoubleToDisp (Total_run);
//module.setDisplayToDecNumber(Total_run, 1, false);
delay (1000);
// if the status is zero then turn off the screen
if (Sys_status == 0)
{
module.setupDisplay (false, 0);
}

break;

// if the 2nd from the left is pressed
case 0b00000010:
// we blink with the corresponding diode the rest we extinguish
module.setLEDs (0);
module.setLED (Red, 1);
module.clearDisplay ();
// display the maximum speed
DoubleToDisp (Max_speed);
//module.setDisplayToDecNumber(Max_speed, 1, false);
delay (1000);
if (Sys_status == 0)
{
module.setupDisplay (false, 0);
}
break;

// if the 3rd from the left is pressed
case 0b00000100:
// we blink with the corresponding diode the rest we extinguish
module.setLEDs (0);
module.setLED (Red, 2);
module.clearDisplay ();
// how many days have passed since we
started Day_passed = millis () / 86400000.00;
// if whole days have passed
if (Day_passed> 1)
{

// consider the total mileage in kilometers
Total_run = Round_counter * (double) One_round / 1000.00;
// print the average daily run
DoubleToDisp (Total_run / Day_passed);
delay (1000);
module.clearDisplay ();
}
else // if the whole day has not passed
{
module.setDisplayToString ("0", 0);
}
if (Sys_status == 0)
{
module.setupDisplay (false, 0);
}

break;

// if the 4th from the left is pressed
case 0b00001000:
// blink with the corresponding diode;
extinguish the rest extinguish module.setLEDs (0);
module.setLED (Red, 3);
module.clearDisplay ();
// count and display the average speed per lap :)
DoubleToDisp (All_speeds / Round_counter);

delay (1000);
if (Sys_status == 0)
{
module.setupDisplay (false, 0);
}
break;

// if the 5th from the left is pressed
case 0b00010000:
// we blink with the corresponding diode the rest we extinguish
module.setLEDs (0);
module.setLED (Red, 4);
module.clearDisplay ();
// how many days have passed since we
started Day_passed = millis () / 86400000.00;
DoubleToDisp (Day_passed);

delay (1000);
if (Sys_status == 0)
{
module.setupDisplay (false, 0);
}
break;

// if left 6th pressed
case 0b00100000:
// blink with the appropriate diode; the rest we extinguish
module.setLEDs (0);
module.setLED (Red, 4);
module.clearDisplay ();
// print the number of circles
module.setDisplayToDecNumber (Round_counter, 0, false);

delay (1000);
if (Sys_status == 0)
{
module.setupDisplay (false, 0);
}
break;

// if the 1st from the right is pressed
case 0b10000000:
// we blink with the corresponding diode the rest we extinguish
module.setLEDs (0);
module.setLED (Red, 7);
module.clearDisplay ();
// display the current humidity on the display
ReadDHT (); // read the sensor
// if there are no errors
if (bGlobalErr == 0)
{
byte humidity = dht_dat [0];
module.setDisplayToDecNumber (humidity, 0, false);
// DoubleToDisp (humidity);
}
delay (1000);
if (Sys_status == 0)
{
module.setupDisplay (false, 0);
}
break;

// if the 2nd from the right is pressed
case 0b01000000:
// we blink with the corresponding diode the rest we extinguish
module.setLEDs (0);
module.setLED (Red, 6);
module.clearDisplay ();
// display the current temperature
ReadDHT (); // read the sensor
// if there are no errors
if (bGlobalErr == 0)
{
byte temperature = dht_dat [2];
module.setDisplayToDecNumber (temperature, 0, false);
}
delay (1000);
if (Sys_status == 0)
{
module.setupDisplay (false, 0);
}
break;
}
// end of buttons
/ **************** end of buttons *************************** ************ //

/ ***************** here is the whole logic of reading information *************** *********** /

// read the data - we connect the hall sensor to the 3 digital port
int sensorValue = digitalRead (Hall_port);

// if the state of the sensor has changed
if (sensorValue! = PrevValue)
{
PrevValue = sensorValue;
// if the sensor gives 0 i.e. magnet here
if (! sensorValue)
{
Sys_status = 2;

// turn on the display if the pause was longer than the interval slip and it has already turned off
if (time_span_mon> Do_sleeping_interval)
{
module.setupDisplay (true, 7);
}
// add the counter
Round_counter ++;

// current time in milliseconds
unsigned long time_now = millis ();

// consider the time difference in ms between the current and the previous
unsigned long time_span = time_now - Prev_time;
// current write to the previous
Prev_time = time_now;
// if the pause is less than 10 seconds, then we recognize that the krutitsa’s wheel
if (time_span <Wait_interval)
{
if (Curr_speed> 10.00)
{
module.clearDisplay ();
}

Curr_speed = One_round / time_span * 1000 * 3.6; // kilometers per hour we get the speed
All_speeds + = Curr_speed; // to calculate the average

// for maximum check
if (Curr_speed> Max_speed)
Max_speed = Curr_speed;

byte led_speed = map (Curr_speed, 0, Max_led_light_speed, 0, 7);
// call the ice highlight
Led_speed_light (led_speed);

// debug
// Serial.print ("Round_counter =");
//Serial.println(Round_counter);
//Serial.print ("curr_speed (km / h) =");
//Serial.println(Curr_speed, 4);

// display the current speed
DoubleToDisp (Curr_speed);

}
// if the pause is more than 10 seconds, then we assume that the wheel stood before this
// we don’t take into account the data of the previous time, but we begin
// to consider the difference first
else
{
Prev_time = time_now;

// debug
//Serial.print ("time_span reset");
}

}

}

else // if the sensor status has not changed
{
// we take the current time
unsigned long time_now = millis ();

// time difference between the current and the previous for post monitoring in ms, consider
time_span_mon = time_now - Prev_time;

// if the interval between touches is more than 10 seconds, then we assume that the wheel
// has stopped and display 0 (the interval must be clarified)
if (time_span_mon> Wait_interval && Sys_status! = 1)
{
Sys_status = 1;
Curr_speed = 0;
module.clearDisplay ();
module.setDisplayDigit (0, 7, true);
module.setLEDs (0);
}

// if the pause is more than 60 seconds, then extinguish the screen
// if the interval between touches is more than 10 seconds, then we assume that the wheel
// has stopped and output 0 (the interval must be specified)
if (time_span_mon> Do_sleeping_interval && Sys_status! = 0)
{
Sys_status = 0;
module.setupDisplay (false, 0);
}

}

}
/// diode illumination depending on the wheel rotation speed
// function
void Led_speed_light (byte led_speed)
{
constrain (led_speed, 0, 7);

switch (led_speed) {
case 0:
module.setLEDs (0);
module.setLED (Green, 0);
delay (100);
module.setLED (0, 0);
break;
case 1:
module.setLEDs (0);
module.setLED (Green, 0);
module.setLED (Green, 1);
delay (100);
module.setLED (0, 1);
break;
case 2:
module.setLEDs (0);
module.setLED (Green, 0);
module.setLED (Green, 1);
module.setLED (Green, 2);
delay (100);
for (int i = 2; i> 0; i -) {
module.setLED (0, i);
delay (50);
}
break;
case 3:
module.setLEDs (0);
module.setLED (Green, 0);
module.setLED (Green, 1);
module.setLED (Green, 2);
module.setLED (Green, 3);
delay (100);
for (int i = 3; i> 0; i -) {
module.setLED (0, i);
delay (50);
}
break;
case 4:
module.setLEDs (0);
module.setLED (Green, 0);
module.setLED (Green, 1);
module.setLED (Green, 2);
module.setLED (Green, 3);
module.setLED (Green, 4);
delay (100);
for (int i = 4; i> 0; i -) {
module.setLED (0, i);
delay (50);
}
break;
case 5:
module.setLEDs (0);
module.setLED (Green, 0);
module.setLED (Green, 1);
module.setLED (Green, 2);
module.setLED (Green, 3);
module.setLED (Green, 4);
module.setLED (Red, 5);
delay (100);
for (int i = 5; i> 0; i -) {
module.setLED (0, i);
delay (50);
}
break;
case 6:
module.setLEDs (0);
module.setLED (Green, 0);
module.setLED (Green, 1);
module.setLED (Green, 2);
module.setLED (Green, 3);
module.setLED (Green, 4);
module.setLED (Red, 5);
module.setLED (Red, 6);
delay (100);
for (int i = 6; i> 0; i -) {
module.setLED (0, i);
delay (50);
}
break;
case 7:
module.setLEDs (0);
module.setLED (Green, 0);
module.setLED (Green, 1);
module.setLED (Green, 2);
module.setLED (Green, 3);
module.setLED (Green, 4);
module.setLED (Red, 5);
module.setLED (Red, 6);
module.setLED (Red, 7);
delay (100);
for (int i = 7; i> 0; i -) {
module.setLED (0, i);
delay (50);
}

break;
}

}

// function displays LKM1638 fractional values ​​(accuracy - 2 digits)
void DoubleToDisp (double num)
{
double mult_num = (double) num * 100.00;
long int_mult_num = (long) mult_num;
String string_mult_num = String (int_mult_num);
int length = string_mult_num.length ();
char str [length + 1];

string_mult_num.toCharArray (str, length + 1);

// if the number was less than 1 then the collective farm gag to derive the correct fraction
if (num <1)
{
module.setDisplayDigit (0, 5, true);
module.setDisplayDigit (str [0], 6, false);
module.setDisplayDigit (str [1], 7, false);
}

else // if more than 1 then print
{
for (int i = 0; i {
if (i == length - 3) {
module.setDisplayDigit (str [i], i + 8-length, true);
}
else
{
module.setDisplayDigit (str [i], i + 8-length, false);
}
}
}

}

/ ****** functions for DHT11 *** /

void InitDHT () {

pinMode (dht_dpin, OUTPUT);

digitalWrite (dht_dpin, HIGH);

}

void ReadDHT () {

bGlobalErr = 0;

byte dht_in;

byte i;

digitalWrite (dht_dpin, LOW);

delay (20);

digitalWrite (dht_dpin, HIGH);

delayMicroseconds (40);

pinMode (dht_dpin, INPUT);

// delayMicroseconds (40);

dht_in = digitalRead (dht_dpin);

if (dht_in) {

bGlobalErr = 1;

return

}

delayMicroseconds (80);

dht_in = digitalRead (dht_dpin);

if (! dht_in) {

bGlobalErr = 2;

return

}

delayMicroseconds (80);

for (i = 0; i <5; i ++)

dht_dat [i] = read_dht_dat ();

pinMode (dht_dpin, OUTPUT);

digitalWrite (dht_dpin, HIGH);

byte dht_check_sum =

dht_dat [0] + dht_dat [1] + dht_dat [2] + dht_dat [3];

if (dht_dat [4]! = dht_check_sum)

{
bGlobalErr = 3;
}

};

byte read_dht_dat () {

byte i = 0;

byte result = 0;

for (i = 0; i <8; i ++) {

while (digitalRead (dht_dpin) == LOW);

delayMicroseconds (30);

if (digitalRead (dht_dpin) == HIGH)

result | = (1 << (7-i));

while (digitalRead (dht_dpin) == HIGH);

}

return result;

}

/ ****** end of functions for DHT11 **** /



Next, a few videos to form a general idea of ​​the resulting design.



Results, interesting facts and conclusions



  1. From early morning, squirrel usually runs at an average speed of ~ 5-6 km / h (apparently, in search of food and a better share).
  2. After a mandatory day's sleep and eating high-calorie nuts, the squirrel's speed drops to 3-4 km / h (I’ve already found food, I’ve done everything important in life).
  3. With a nearby vacuum cleaner, the speed suddenly increases to 12-14 km / h! Although I met somewhere on foreign resources about the maximum speed of squirrels at 30 km / h, I can responsibly declare that all this is a lie. Well, or my squirrel does not belong to the breed of hounds (African Olympic).
  4. The maximum lap speed is 18 km / h.
  5. The average daily run of a squirrel, measured over two months, is ~ 15 kilometers.

These data can be used as a feasibility study for a project to create generating capacities - mini power plants for heating a squirrel house in winter or lighting in the dark. Or for a school computer science project.

Also popular now: