📜 ⬆️ ⬇️

Wheel speed counter squirrel

An ordinary analog protein settled in my house.
She lived in a cage, lived for herself, spun her wheel. With different speeds. In the morning, usually more energetic, in the evening, tired, slow down. And all this time one question did not give me rest. And how fast is the squirrel in the wheel?
I thought long and once ...
I decided to make a wheel speed counter .

The photo is a prototype. Therefore, not everything is done neatly.

image
')
Sortsy, video and final measurement results under the cat.


Components.


  1. Arduino Uno.
  2. Screen LKM-1638.
  3. Hall sensor (any suitable).
  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 immediately pump the squirrel cage into a smart one! Ordered on dx.com.

He did everything himself, with his own hands.

I chose a ready-made Arduino Uno board, so as not to waste time on wiring, pickling, programmers, etc., and immediately begin to implement the plan. The screen took the LKM-1638, because it is bright, chotke , with large and clear numbers.

Along the way, the buttons on the display were useful for displaying statistical information.

Pressing the corresponding button displays:


  1. Total mileage in kilometers.
  2. Maximum speed of the circle.
  3. Average daily mileage in kilometers.
  4. Average speed per lap.
  5. Uptime in days
  6. 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 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 a semblance of a digital tachometer. By the principle: the more diodes are lit, the closer the current speed is to the maximum, the smaller, the lower the minimum speed.

I gathered everything together, connected the wires and started writing the code.
The library for the screen took ready here .
For temperature and humidity sensor examples and library 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 cell at the point closest to the wheel. There is a small but very strong neodymium magnet on the wheel, which I pulled out of the head of an old DVD drive.
When the wheel rotates, with each integer turn of the magnet for a short time changes the magnetic field in the radius of measurement of the Hall sensor, changing in the latter state from 1 to 0. With the reverse arrangement of the sensor, the value would change from 0 to 1 (which is probably more correct ), but then the sensor would be worse kept on the cell.
The difference in time between the two occurrences of a magnet in the area of ​​the sensor is recognized for a whole circle. Further, knowing the inner circumference of the wheel, you can easily calculate the speed of the circle and bring it to the display, which I did.
It should be noted here that this method of measurement has nuances :
  1. It is considered only a complete revolution of the wheel. If the squirrel scrolled, for example, 0.9 turnover and jumped out of it, then this turnover is not counted at all. Or if she turned the wheel at 1.9, 2.9, 3.9 ... n.9 revolutions and so repeated many times during the device testing process, then there is a big error in the calculation of 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 rotating wheel in the direction opposite to the rotation, then in opposite direction, if the magnet just passed by the sensor at that time, the magnet passes the sensor again in the other direction, erroneously fixing the whole full turn of the wheel.

Measurement errors in these cases can be minimized by increasing the number of sensors, for example, to 4 and placing
they are on opposite sides of the circle (in? / 2 radians or 90 degrees). Then you can take into account not only every quarter turn, which will definitely increase the accuracy of measurements, but also know the direction of the wheel rotation (i.e. where the protein 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. By the time available, I will optimize it.

All code
/ *
sketch for counting wheel speed
and output values ​​to 8 digital screen
plus output current temperature and humidity
release
* /

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

byte bGlobalErr;

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

/ ******* variables for the digital module ****************** /

// library for 8-segment digital module
#include "TM1638.h"
// Pins in the data pin 5 module, 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 the Hall sensor ******************** /

// wheel circumference in meters (it is necessary to clarify)
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 larger wheel is recognized only as stopped,
// if less, then it is considered that it is cool
#define Wait_interval 10000
// interval after which the screen turns off
#define 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

// wheel spin count
unsigned long Round_counter = 0;
// store the previous time here to calculate speed
unsigned long Prev_time = 0;
// store the previous time here for continuous monitoring
unsigned long Prev_time_mon = 0;
// here is the data to monitor 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 to be counted by pressing buttons
double Total_run = 0;
// speed of all circles here put to calculate the average
double All_speeds = 0;
// how many days have passed
double Day_passed = 0;
// variable to store the system state
// 0- system in power saving and standby mode - the wheel has not been cool for a long time - the screen is off
// 1- the wheel has just ceased to spin
// 2- sprint wheel
byte Sys_status = 0;

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

void setup () {

Serial.begin (9600);
InitDHT (); // initialisation sensor DHT11
Serial.println (“Run!”);

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

}

void loop () {

if (Sys_status == 0)
{
// If sleep mode, then less often we poll sensor
delay (Delay_in_sleep);
}
else
{
// if the operating mode is more often we poll the sensor
delay (Delay_in_work);
}
/ ************** start 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 leftmost button is pressed
case 0b00000001:

// we blink the corresponding diode the others we extinguish
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 is pressed from the left
case 0b00000010:
// we blink the corresponding diode the others 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 left 3 is pressed
case 0b00000100:
// we blink the corresponding diode the others we extinguish
module.setLEDs (0);
module.setLED (Red, 2);
module.clearDisplay ();
// how many days have we passed since we started
Day_passed = millis () / 86400000.00;
// if the whole day passed
if (Day_passed> 1)
{

// count 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 4th left is pressed
case 0b00001000:
// we blink the corresponding diode the others we 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 5th left is pressed
case 0b00010000:
// we blink the corresponding diode the others we extinguish
module.setLEDs (0);
module.setLED (Red, 4);
module.clearDisplay ();
// how many days have we passed since we started
Day_passed = millis () / 86400000.00;
DoubleToDisp (Day_passed);

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

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

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

// if 1st is pressed on the right
case 0b10000000:
// we blink the corresponding diode the others we extinguish
module.setLEDs (0);
module.setLED (Red, 7);
module.clearDisplay ();
// display current humidity
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 2nd is pressed on the right
case 0b01000000:
// we blink the corresponding diode the others 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 info ************************** /

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

// if the sensor state 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 we take in millisecs
unsigned long time_now = millis ();

// count the difference in ms in time between the current and previous
unsigned long time_span = time_now - Prev_time;
// current write to previous
Prev_time = time_now;
// if the pause is less than 10 seconds, then we recognize that the wheel is cool
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 speed
All_speeds + = Curr_speed; // to calculate the average

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

byte led_speed = map (Curr_speed, 0, Max_led_light_speed, 0, 7);
// call the backlight
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 current speed
DoubleToDisp (Curr_speed);

}
// if the pause is more than 10 seconds, then we believe that the wheel stood before
// and we do not take into account the previous time data, but we start
// count the difference first
else
{
Prev_time = time_now;

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

}

}

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

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

// if the interval between touches is more than 10 seconds - then we assume that the wheel
// stopped and output 0 (interval needs to 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
// if the interval between touches is more than 10 seconds - then we assume that the wheel
// stopped and output 0 (interval needs to be clarified)
if (time_span_mon> Do_sleeping_interval && Sys_status! = 0)
{
Sys_status = 0;
module.setupDisplay (false, 0);
}

}

}
/// diode illumination depending on wheel 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 characters)
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 to stop 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 output
{
for (int i = 0; i <length; 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 little video to form a general idea of ​​the resulting design.



Results, interesting facts and conclusions



  1. From early morning the 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 daytime sleep and taking high-calorie nuts, the squirrel speed drops to 3-4 km / h (I have already found food, I have done everything important in life).
  3. When running near the vacuum cleaner, the speed EXTREMELY 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 false. Well, or my squirrel does not belong to the breed of hounds (African Olympic).
  4. The maximum speed of the circle is 18 km / h.
  5. The average daily mileage of a squirrel, measured in two months, is ~ 15 kilometers.

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

Source: https://habr.com/ru/post/159177/


All Articles