A few weeks ago I wrote about Hytera PD-785G as I seen it and used it.
Today I have to make a follow up about things that I disliked before, things that are now history due to the recent |firmware update. The FW I made is to rev. 6.05, the latest one, a firmware update that solved a lot of problems.
The first one is that now we can scan on the same list both digital and analog channels. This is great news because I missed the analog comms when scanning DMR channels, which are my favorites.
The second is the extended FPP, a.k.a. Front Panel Programming. First time, with the rev.5.01, one can set only the receiving and the transmitting frequencies. The CTCSS, Colour code and other settings simply couldn't be done via front panel.
Now, on the analog channels, the user can programm the Rx CTCSS and the Tx CTCSS, separately.
On the digital channels, can be programmed: Slot, Colour code, TX contact and Rx Group list. This means that some settings are to be done via PC CPS but, hey, is a great improvement for real life use of the radio!
Also, some bugs was solved. Before, it was a problem to set on a key two functions (ex: Alarm enable for short press and Alarm off for long press) but now that problem is solved.
It's impressive!
27 ianuarie 2015
18 ianuarie 2015
CTCSS decoder with Arduino
From a friend of mine i got an ideea for a complex repeater controller, able to work with 4 radios. Two of them forming a UHF repeater and, at least, two other, forming a second repeater or simplex radios for linking the first repeater with other remote repeaters.
But this is not the subject here...
The first step was to check the possibility to decode CTCSS and to generate it back. But decoding was the biggest challenge.
I was looking for some CML circuits but the representative here asked me to buy a large quantity at a big price.
So I was turning to my Arduino trying to figure it out how to make it able to decode CTCSS in no more than 100 msec.
Finally, I did it by measuring the pulses between interrupts and find this method pretty nice and precise, at least for a crystal resonator driven Arduino.
*** Refinements of the initial code was further made by Jean-Jacques ON7EQ and Paul ON4ADI. Please, read to the end.
Here is the code, just copy/paste it in the Arduino IDE:
// Frequency counter sketch, for measuring frequencies low enough to execute an interrupt for each cycle
// Connect the frequency source to the INT0 pin (digital pin 2 on an Arduino Uno)
// By Adrian, YO3HJV
// This work is released to Public Domain
// First published on January, 18th 2015 on
// http://yo3hjv.blogspot.ro/2015/01/ctcss-decoder-with-arduino.html
// This code can be used for free but I will appreciate if you mention the author.
// 73 de Adrian, YO3HJV
#include
#include
/ *May be uncomment for standard Arduino LCD library.
I am using a weird library here... */
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // set for my configuration. Let PIN2 to be the input for the signal.
volatile unsigned long timpPrimImpuls;
volatile unsigned long timpUltimImpuls;
volatile unsigned long numImpuls;
void setup()
{
//Serial.begin(19200); // print for debugging. Uncomment if necessary
lcd.begin(16, 2);
}
// Measure the frequency over the specified sample time in milliseconds, returning the frequency in Hz
float readFrequency(unsigned int sampleTime)
{
numImpuls = 0; // start a new reading
attachInterrupt(0, counter, RISING); // enable the interrupt
delay(sampleTime);
detachInterrupt(0);
return (numImpuls < 3) ? 0 : (996500.0 * (float)(numImpuls - 2))/(float)(timpUltimImpuls - timpPrimImpuls);
}
// NOTE: 996500.0 is the value find by me. The theoretic value is "1000000000.0"
// Start with this value and check the precision against a good frequency meter.
void loop()
{
float freq = readFrequency(100);
lcd.setCursor(0, 0);
lcd.print("Freq: ");
lcd.print (freq);
lcd.print(" ");
lcd.print("Hz ");
lcd.setCursor (0, 1);
// Too low but over 10 Hz
if ((freq > 10) && (freq < 65.8))
{
lcd.print(" TOO LOW ");
//DO SOMETHING
}
else if ((freq > 66.00) && (freq < 68.00))
{
lcd.print("CT: 67.0 Hz, XZ ");
//DO SOMETHING
}
else if ((freq > 68.30) && (freq < 70.30))
{
lcd.print("CT: 69.3 Hz, WZ ");
//DO SOMETHING
}
else if ((freq > 70.90) && (freq < 72.90))
{
lcd.print("CT: 71.9 Hz, XA ");
//DO SOMETHING
}
else if ((freq > 73.40) && (freq < 75.40))
{
lcd.print("CT: 74.4 Hz, WA ");
//DO SOMETHING
}
else if ((freq > 76.00) && (freq < 78.00))
{
lcd.print("CT: 77.0 Hz, XB ");
//DO SOMETHING
}
else if ((freq > 78.70) && (freq < 79.70))
{
lcd.print("CT: 79.70 Hz, WB ");
//DO SOMETHING
}
else if ((freq > 81.50) && (freq < 83.50))
{
lcd.print("CT: 82.5 Hz, YZ ");
//DO SOMETHING
}
else if ((freq > 84.30) && (freq < 86.50))
{
lcd.print("CT: 85.4 Hz, YA ");
//DO SOMETHING
}
else if ((freq > 87.40) && (freq < 89.60))
{
lcd.print("CT: 88.5 Hz, YB ");
//DO SOMETHING
}
else if ((freq > 90.40) && (freq < 92.60))
{
lcd.print("CT: 91.5 Hz, ZZ ");
//DO SOMETHING
}
else if ((freq > 93.7) && (freq < 95.90))
{
lcd.print("CT: 94.8 Hz, ZA ");
//DO SOMETHING
}
else if ((freq > 96.30) && (freq < 98.5))
{
lcd.print("CT: 97.4 Hz, ZB ");
//DO SOMETHING
}
else if ((freq > 99.00) && (freq < 101.00))
{
lcd.print("CT: 100.0 Hz, 1Z ");
//DO SOMETHING
}
else if ((freq > 102.40) && (freq < 104.60))
{
lcd.print("CT: 103.5 Hz, 1A ");
//DO SOMETHING
}
else if ((freq > 106.10) && (freq < 108.30))
{
lcd.print("CT: 107.2 Hz, 1B ");
//DO SOMETHING
}
else if ((freq > 109.80) && (freq < 112.00))
{
lcd.print("CT: 110.9 Hz, 2Z ");
//DO SOMETHING
}
else if ((freq > 113.60) && (freq < 116.00))
{
lcd.print("CT: 114.8 Hz, 2A ");
//DO SOMETHING
}
else if ((freq > 117.60) && (freq < 119.90))
{
lcd.print("CT: 118.8 Hz, 2B ");
//DO SOMETHING
}
else if ((freq > 122.00) && (freq < 124.00))
{
lcd.print("CT: 123.0 Hz, 3Z ");
//DO SOMETHING
}
else if ((freq > 126.20) && (freq < 128.40))
{
lcd.print("CT: 127.3 Hz, 3A ");
}
else if ((freq > 130.40) && (freq < 133.00))
{
lcd.print("CT: 131.8 Hz, 3B ");
}
else if ((freq > 135.00) && (freq < 138.00))
{
lcd.print("CT: 136.5 Hz, 4Z ");
//DO SOMETHING
}
else if ((freq > 140.00) && (freq < 142.80))
{
lcd.print("CT: 141.3 Hz, 4A ");
//DO SOMETHING
}
else if ((freq > 145.00) && (freq < 147.80))
{
lcd.print("CT: 146.2 Hz, 4B ");
//DO SOMETHING
}
else if ((freq > 150.00) && (freq < 152.80))
{
lcd.print("CT: 151.4 Hz, 5Z ");
//DO SOMETHING
}
else if ((freq > 156.00) && (freq < 158.80))
{
lcd.print("CT: 157.7 Hz, 5A ");
//DO SOMETHING
}
// NON-STANDARD
else if ((freq > 159.00) && (freq < 161.00))
{
lcd.print("CT: 159.8 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 161.00) && (freq < 163.50))
{
lcd.print("CT: 162.2 Hz, 5B ");
//DO SOMETHING
}
// NON-STANDARD
else if ((freq > 164.00) && (freq < 166.30))
{
lcd.print("CT: 165.5 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 166.60) && (freq < 169.00))
{
lcd.print("CT: 167.9 Hz, 6Z ");
//DO SOMETHING
}
// NON-STANDARD
else if ((freq > 170.00) && (freq < 172.40))
{
lcd.print("CT: 171.3 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 172.60) && (freq < 175.00))
{
lcd.print("CT: 173.8 Hz, 6A ");
//DO SOMETHING
}
//NON-STANDARD
else if ((freq > 176.00) && (freq < 178.50))
{
lcd.print("CT: 177.3 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 178.6) && (freq < 181.00))
{
lcd.print("CT: 179.9 Hz, 6Z ");
//DO SOMETHING
}
//NON-STANDARD
else if ((freq > 182.00) && (freq < 184.80))
{
lcd.print("CT: 183.5 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 185.00) && (freq < 187.50))
{
lcd.print("CT: 186.2 Hz, 7Z ");
//DO SOMETHING
}
//NON-STANRDARD
else if ((freq > 188.40) && (freq < 191.30))
{
lcd.print("CT: 189.9 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 191.00) && (freq < 194.00))
{
lcd.print("CT: 192.8 Hz, 7A ");
//DO SOMETHING
}
//NON-STANDARD
else if ((freq > 195.40) && (freq < 198.00))
{
lcd.print("CT: 196.6 Hz, -- ");
//DO SOMETHING
}
//NON-STANDARD
else if ((freq > 198.30) && (freq < 201.00))
{
lcd.print("CT: 199.5 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 202.00) && (freq < 204.00))
{
lcd.print("CT: 203.5 Hz, M1 ");
//DO SOMETHING
}
else if ((freq > 205.00) && (freq < 208.00))
{
lcd.print("CT: 206.5 Hz, 8Z ");
//DO SOMETHING
}
else if ((freq > 209) && (freq < 212.00))
{
lcd.print("CT: 210.7 Hz, M2 ");
//DO SOMETHING
}
else if ((freq > 217.00) && (freq < 219.30))
{
lcd.print("CT: 218.1 Hz, M3 ");
}
else if ((freq > 224.00) && (freq < 227.00))
{
lcd.print("CT: 225.7 Hz, M4 ");
}
else if ((freq > 227.60) && (freq < 231.30))
{
lcd.print("CT: 229.1 Hz, 9Z ");
//DO SOMETHING
}
else if ((freq > 231.70) && (freq < 235.00))
{
lcd.print("CT: 233.6 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 239.60) && (freq < 243.00))
{
lcd.print("CT: 241.8 Hz, M6 ");
//DO SOMETHING
}
else if ((freq > 248.00) && (freq < 252.00))
{
lcd.print("CT: 250.3 Hz, M7 ");
//DO SOMETHING
}
else if ((freq > 252.70) && (freq < 256.80))
{
lcd.print("CT: 254.1 Hz, 0Z ");
//DO SOMETHING
}
else if (freq > 256.80)
{
lcd.print(" TOO HIGH ");
//DO SOMETHING
}
else
{
lcd.setCursor (0, 1);
lcd.print(" NOISE ");
// or, comment the line above and
// uncomment the line below for an empty LCD line
// lcd.print(" ");
}
delay(50);
// lcd.clear(); //Not necessary. uncomment but will flicker!
} //END OF LOOP
void counter()
{
unsigned long now = micros();
if (numImpuls == 1)
{
timpPrimImpuls = now;
}
else
{
timpUltimImpuls = now;
}
++numImpuls;
}
After fine tuning the code, the second issue was to make the Arduino read from real life signals, a.k.a from the discriminator (FM detector).
But there, unfortunately, there is also "noise" above 300 Hz from the voice and the Arduino will tend to detect it too, asuming that we have a 5Vpp signal on pin 2 of the UNO board.
Therefore, a preamplifier and a Low Pass Filter to cut all the frequenciesbelow* above 260 Hz with at least 12db/octave.
I will not bother you with more than necessary details, I post the schematics here and if someone has questions i will answer.
73 de Adrian YO3HJV
A "Later Edit" is a must!
After some emails with Carlo IU1DOR found some mistakes in the schematics. One is about the parallel diodes at U3a which are drawn as conductive in one sense; must be corrected as anti-parallel diodes.
The second one is R10 which is not in series with that diodes.
Here is the original part from Motorola GM300 schematics:
Anyway, Carlo kindly give me the permission to post here the PCB layout for the filter (TNX!):
The code is available on GITHUB.
*-TNX IU1DOR for correction!
LATER EDIT (15 feb. 2016)
I recently received a e-mail from Jean-Jacques ON7EQ who pointed me to an upgrade to this project.
The upgrade was made possible by the work of Paul ON4ADI who refine the code and added a necessary software filter to make the CTCSS Arduino decoder able to withstand to real world conditions.
The project of the new CTCSS decoder can be seen here.
But this is not the subject here...
The first step was to check the possibility to decode CTCSS and to generate it back. But decoding was the biggest challenge.
I was looking for some CML circuits but the representative here asked me to buy a large quantity at a big price.
So I was turning to my Arduino trying to figure it out how to make it able to decode CTCSS in no more than 100 msec.
Finally, I did it by measuring the pulses between interrupts and find this method pretty nice and precise, at least for a crystal resonator driven Arduino.
*** Refinements of the initial code was further made by Jean-Jacques ON7EQ and Paul ON4ADI. Please, read to the end.
Here is the code, just copy/paste it in the Arduino IDE:
// Frequency counter sketch, for measuring frequencies low enough to execute an interrupt for each cycle
// Connect the frequency source to the INT0 pin (digital pin 2 on an Arduino Uno)
// By Adrian, YO3HJV
// This work is released to Public Domain
// First published on January, 18th 2015 on
// http://yo3hjv.blogspot.ro/2015/01/ctcss-decoder-with-arduino.html
// This code can be used for free but I will appreciate if you mention the author.
// 73 de Adrian, YO3HJV
#include
#include
/ *May be uncomment for standard Arduino LCD library.
I am using a weird library here... */
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // set for my configuration. Let PIN2 to be the input for the signal.
volatile unsigned long timpPrimImpuls;
volatile unsigned long timpUltimImpuls;
volatile unsigned long numImpuls;
void setup()
{
//Serial.begin(19200); // print for debugging. Uncomment if necessary
lcd.begin(16, 2);
}
// Measure the frequency over the specified sample time in milliseconds, returning the frequency in Hz
float readFrequency(unsigned int sampleTime)
{
numImpuls = 0; // start a new reading
attachInterrupt(0, counter, RISING); // enable the interrupt
delay(sampleTime);
detachInterrupt(0);
return (numImpuls < 3) ? 0 : (996500.0 * (float)(numImpuls - 2))/(float)(timpUltimImpuls - timpPrimImpuls);
}
// NOTE: 996500.0 is the value find by me. The theoretic value is "1000000000.0"
// Start with this value and check the precision against a good frequency meter.
void loop()
{
float freq = readFrequency(100);
lcd.setCursor(0, 0);
lcd.print("Freq: ");
lcd.print (freq);
lcd.print(" ");
lcd.print("Hz ");
lcd.setCursor (0, 1);
// Too low but over 10 Hz
if ((freq > 10) && (freq < 65.8))
{
lcd.print(" TOO LOW ");
//DO SOMETHING
}
else if ((freq > 66.00) && (freq < 68.00))
{
lcd.print("CT: 67.0 Hz, XZ ");
//DO SOMETHING
}
else if ((freq > 68.30) && (freq < 70.30))
{
lcd.print("CT: 69.3 Hz, WZ ");
//DO SOMETHING
}
else if ((freq > 70.90) && (freq < 72.90))
{
lcd.print("CT: 71.9 Hz, XA ");
//DO SOMETHING
}
else if ((freq > 73.40) && (freq < 75.40))
{
lcd.print("CT: 74.4 Hz, WA ");
//DO SOMETHING
}
else if ((freq > 76.00) && (freq < 78.00))
{
lcd.print("CT: 77.0 Hz, XB ");
//DO SOMETHING
}
else if ((freq > 78.70) && (freq < 79.70))
{
lcd.print("CT: 79.70 Hz, WB ");
//DO SOMETHING
}
else if ((freq > 81.50) && (freq < 83.50))
{
lcd.print("CT: 82.5 Hz, YZ ");
//DO SOMETHING
}
else if ((freq > 84.30) && (freq < 86.50))
{
lcd.print("CT: 85.4 Hz, YA ");
//DO SOMETHING
}
else if ((freq > 87.40) && (freq < 89.60))
{
lcd.print("CT: 88.5 Hz, YB ");
//DO SOMETHING
}
else if ((freq > 90.40) && (freq < 92.60))
{
lcd.print("CT: 91.5 Hz, ZZ ");
//DO SOMETHING
}
else if ((freq > 93.7) && (freq < 95.90))
{
lcd.print("CT: 94.8 Hz, ZA ");
//DO SOMETHING
}
else if ((freq > 96.30) && (freq < 98.5))
{
lcd.print("CT: 97.4 Hz, ZB ");
//DO SOMETHING
}
else if ((freq > 99.00) && (freq < 101.00))
{
lcd.print("CT: 100.0 Hz, 1Z ");
//DO SOMETHING
}
else if ((freq > 102.40) && (freq < 104.60))
{
lcd.print("CT: 103.5 Hz, 1A ");
//DO SOMETHING
}
else if ((freq > 106.10) && (freq < 108.30))
{
lcd.print("CT: 107.2 Hz, 1B ");
//DO SOMETHING
}
else if ((freq > 109.80) && (freq < 112.00))
{
lcd.print("CT: 110.9 Hz, 2Z ");
//DO SOMETHING
}
else if ((freq > 113.60) && (freq < 116.00))
{
lcd.print("CT: 114.8 Hz, 2A ");
//DO SOMETHING
}
else if ((freq > 117.60) && (freq < 119.90))
{
lcd.print("CT: 118.8 Hz, 2B ");
//DO SOMETHING
}
else if ((freq > 122.00) && (freq < 124.00))
{
lcd.print("CT: 123.0 Hz, 3Z ");
//DO SOMETHING
}
else if ((freq > 126.20) && (freq < 128.40))
{
lcd.print("CT: 127.3 Hz, 3A ");
}
else if ((freq > 130.40) && (freq < 133.00))
{
lcd.print("CT: 131.8 Hz, 3B ");
}
else if ((freq > 135.00) && (freq < 138.00))
{
lcd.print("CT: 136.5 Hz, 4Z ");
//DO SOMETHING
}
else if ((freq > 140.00) && (freq < 142.80))
{
lcd.print("CT: 141.3 Hz, 4A ");
//DO SOMETHING
}
else if ((freq > 145.00) && (freq < 147.80))
{
lcd.print("CT: 146.2 Hz, 4B ");
//DO SOMETHING
}
else if ((freq > 150.00) && (freq < 152.80))
{
lcd.print("CT: 151.4 Hz, 5Z ");
//DO SOMETHING
}
else if ((freq > 156.00) && (freq < 158.80))
{
lcd.print("CT: 157.7 Hz, 5A ");
//DO SOMETHING
}
// NON-STANDARD
else if ((freq > 159.00) && (freq < 161.00))
{
lcd.print("CT: 159.8 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 161.00) && (freq < 163.50))
{
lcd.print("CT: 162.2 Hz, 5B ");
//DO SOMETHING
}
// NON-STANDARD
else if ((freq > 164.00) && (freq < 166.30))
{
lcd.print("CT: 165.5 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 166.60) && (freq < 169.00))
{
lcd.print("CT: 167.9 Hz, 6Z ");
//DO SOMETHING
}
// NON-STANDARD
else if ((freq > 170.00) && (freq < 172.40))
{
lcd.print("CT: 171.3 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 172.60) && (freq < 175.00))
{
lcd.print("CT: 173.8 Hz, 6A ");
//DO SOMETHING
}
//NON-STANDARD
else if ((freq > 176.00) && (freq < 178.50))
{
lcd.print("CT: 177.3 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 178.6) && (freq < 181.00))
{
lcd.print("CT: 179.9 Hz, 6Z ");
//DO SOMETHING
}
//NON-STANDARD
else if ((freq > 182.00) && (freq < 184.80))
{
lcd.print("CT: 183.5 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 185.00) && (freq < 187.50))
{
lcd.print("CT: 186.2 Hz, 7Z ");
//DO SOMETHING
}
//NON-STANRDARD
else if ((freq > 188.40) && (freq < 191.30))
{
lcd.print("CT: 189.9 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 191.00) && (freq < 194.00))
{
lcd.print("CT: 192.8 Hz, 7A ");
//DO SOMETHING
}
//NON-STANDARD
else if ((freq > 195.40) && (freq < 198.00))
{
lcd.print("CT: 196.6 Hz, -- ");
//DO SOMETHING
}
//NON-STANDARD
else if ((freq > 198.30) && (freq < 201.00))
{
lcd.print("CT: 199.5 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 202.00) && (freq < 204.00))
{
lcd.print("CT: 203.5 Hz, M1 ");
//DO SOMETHING
}
else if ((freq > 205.00) && (freq < 208.00))
{
lcd.print("CT: 206.5 Hz, 8Z ");
//DO SOMETHING
}
else if ((freq > 209) && (freq < 212.00))
{
lcd.print("CT: 210.7 Hz, M2 ");
//DO SOMETHING
}
else if ((freq > 217.00) && (freq < 219.30))
{
lcd.print("CT: 218.1 Hz, M3 ");
}
else if ((freq > 224.00) && (freq < 227.00))
{
lcd.print("CT: 225.7 Hz, M4 ");
}
else if ((freq > 227.60) && (freq < 231.30))
{
lcd.print("CT: 229.1 Hz, 9Z ");
//DO SOMETHING
}
else if ((freq > 231.70) && (freq < 235.00))
{
lcd.print("CT: 233.6 Hz, -- ");
//DO SOMETHING
}
else if ((freq > 239.60) && (freq < 243.00))
{
lcd.print("CT: 241.8 Hz, M6 ");
//DO SOMETHING
}
else if ((freq > 248.00) && (freq < 252.00))
{
lcd.print("CT: 250.3 Hz, M7 ");
//DO SOMETHING
}
else if ((freq > 252.70) && (freq < 256.80))
{
lcd.print("CT: 254.1 Hz, 0Z ");
//DO SOMETHING
}
else if (freq > 256.80)
{
lcd.print(" TOO HIGH ");
//DO SOMETHING
}
else
{
lcd.setCursor (0, 1);
lcd.print(" NOISE ");
// or, comment the line above and
// uncomment the line below for an empty LCD line
// lcd.print(" ");
}
delay(50);
// lcd.clear(); //Not necessary. uncomment but will flicker!
} //END OF LOOP
void counter()
{
unsigned long now = micros();
if (numImpuls == 1)
{
timpPrimImpuls = now;
}
else
{
timpUltimImpuls = now;
}
++numImpuls;
}
After fine tuning the code, the second issue was to make the Arduino read from real life signals, a.k.a from the discriminator (FM detector).
But there, unfortunately, there is also "noise" above 300 Hz from the voice and the Arduino will tend to detect it too, asuming that we have a 5Vpp signal on pin 2 of the UNO board.
Therefore, a preamplifier and a Low Pass Filter to cut all the frequencies
I will not bother you with more than necessary details, I post the schematics here and if someone has questions i will answer.
73 de Adrian YO3HJV
A "Later Edit" is a must!
After some emails with Carlo IU1DOR found some mistakes in the schematics. One is about the parallel diodes at U3a which are drawn as conductive in one sense; must be corrected as anti-parallel diodes.
The second one is R10 which is not in series with that diodes.
Here is the original part from Motorola GM300 schematics:
Anyway, Carlo kindly give me the permission to post here the PCB layout for the filter (TNX!):
The code is available on GITHUB.
*-TNX IU1DOR for correction!
LATER EDIT (15 feb. 2016)
I recently received a e-mail from Jean-Jacques ON7EQ who pointed me to an upgrade to this project.
The upgrade was made possible by the work of Paul ON4ADI who refine the code and added a necessary software filter to make the CTCSS Arduino decoder able to withstand to real world conditions.
The project of the new CTCSS decoder can be seen here.
03 ianuarie 2015
Mini Meteo Station with Arduino Nano
I finished a complex project, a mini Meteo Station based on Arduino Nano.
On a 4x20 I2C LCD I can see the date, time, inside and outside temperature, relative humidity and air pressure (in hPa).
Main features are:
-Low footprint
-Precise Date and Time with a DS1307 RTC circuit
-Humidity with a DHT-11 sensor
-Baro pressure and interior temperature with a BMP-085
-External temperature with a DS-18B20
-Low power (40 mA from one Li-Ion cell with a DC-DC converter with LCD Backlight on, 30 mA).
-LCD Backlight on demand by push button
Took me about 2 days but I made it!
I intend to make some averages and to compare the pressure over an variable inteval tohave a weather prediction.
Here is the code:
/*
Meteo station with Arduino Nano
by Adrian, YO3HJV
http://yo3hjv.blogspot.com
-- LCD on I2C
-- Barometer and temperature sensor BMP-085 on I2C
-- Humidity sensor DHT-11 on pin D3
-- Dallas DS-18B20 temperature sensor on OneWire, pin D2
-- Backlight Push button on pin A3
*/
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <Barometer.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <dht11.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
#define DHT11PIN 3
dht11 DHT11;
// I2C LCD DISPLAY
#define I2C_ADDR 0x27
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
// ******************
LiquidCrystal_I2C lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin);
// Variabilele pentru Barometru
float temperature; // FLOAT or INT
float tempext; // FLOAT or INT
float pressure;
float atm;
float altitude;
Barometer myBarometer;
int BLpin = A3; // choose the input pin (for a pushbutton)
int val = 0; // variable for reading the pin status
void setup()
{
myBarometer.init();
sensors.begin(); // External Temp sensor
lcd.begin (20,4); // initialize the lcd
lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
pinMode(BLpin, INPUT); // declare pushbutton as input
}
void loop()
{
val = digitalRead(BLpin); // test BLpin
if (val == LOW)
{
lcd.backlight(); // Backlight ON and...
sens(); // ... execute "sens"
}
else // if not ...,
{
lcd.noBacklight(); // Backlight OFF and...
sens(); // ...execute "sens"
}
}
void sens() // Main function
{
tmElements_t tm; //DS1307 RTC
sensors.requestTemperatures(); // Send the command to get ext temperature
tempext = sensors.getTempCByIndex(0);
//BARO + TEMP PE I2C
// we read all the values from barometric sensor but use only temperature and pressure
// we might calculate Dew point!
temperature = myBarometer.bmp085GetTemperature(myBarometer.bmp085ReadUT()); //Get the temperature, bmp085ReadUT MUST be called first
pressure = myBarometer.bmp085GetPressure(myBarometer.bmp085ReadUP()); //Get the temperature
altitude = myBarometer.calcAltitude(pressure); //Uncompensated caculation - in Meters
atm = pressure / 101325;
//Hygro sensor DHT-11
int chk = DHT11.read(DHT11PIN);
if (RTC.read(tm))
{
// Hours, minutes, seconds
lcd.setCursor(12, 0);
lcd2digits(tm.Hour);
lcd.print(":");
lcd2digits(tm.Minute);
lcd.print(":");
lcd2digits(tm.Second);
// Only hours and minutes. Uncomment this and comment above
/* lcd.setCursor(14, 0);
lcd2digits(tm.Hour);
lcd.print(":");
lcd2digits(tm.Minute);
*/
lcd.setCursor(0, 0);
lcd.print(tm.Day);
lcd.print('/');
lcd.print(tm.Month);
lcd.print('/');
lcd.print(tmYearToCalendar(tm.Year));
lcd.setCursor(0, 2);
lcd.print("In: ");
lcd.print(temperature); // we can use [lcd.print(int(temperature))] for value without decimals;
lcd.print(" *C "); // otherwise, temp is with two decimals
lcd.setCursor(0, 3);
lcd.print("Ex: ");
lcd.print(tempext); // the same as above ...
lcd.print(" *C ");
lcd.setCursor(0, 1);
lcd.print("P:");
lcd.print(int((pressure) / 100)); // No decimals pressure
lcd.print(" hPa");
// lcd.print(" ");
lcd.setCursor(12, 1);
lcd.print("Hum. ");
lcd.print((int)DHT11.humidity);
lcd.print("%");
} else
{
lcd.clear();
lcd.print("RTC HW ERROR!!!"); // This is for RTC comm error
delay(10000);
}
delay(200);
} // END "sens" function
//LCD nice time function
void lcd2digits(int number) {
if (number >= 0 && number < 10) {
lcd.print('0'); }
lcd.print(number);
}
73 de Adrian, YO3HJV
On a 4x20 I2C LCD I can see the date, time, inside and outside temperature, relative humidity and air pressure (in hPa).
Main features are:
-Low footprint
-Precise Date and Time with a DS1307 RTC circuit
-Humidity with a DHT-11 sensor
-Baro pressure and interior temperature with a BMP-085
-External temperature with a DS-18B20
-Low power (40 mA from one Li-Ion cell with a DC-DC converter with LCD Backlight on, 30 mA).
-LCD Backlight on demand by push button
Took me about 2 days but I made it!
I intend to make some averages and to compare the pressure over an variable inteval tohave a weather prediction.
Here is the code:
/*
Meteo station with Arduino Nano
by Adrian, YO3HJV
http://yo3hjv.blogspot.com
-- LCD on I2C
-- Barometer and temperature sensor BMP-085 on I2C
-- Humidity sensor DHT-11 on pin D3
-- Dallas DS-18B20 temperature sensor on OneWire, pin D2
-- Backlight Push button on pin A3
*/
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <Barometer.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <dht11.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
#define DHT11PIN 3
dht11 DHT11;
// I2C LCD DISPLAY
#define I2C_ADDR 0x27
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
// ******************
LiquidCrystal_I2C lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin);
// Variabilele pentru Barometru
float temperature; // FLOAT or INT
float tempext; // FLOAT or INT
float pressure;
float atm;
float altitude;
Barometer myBarometer;
int BLpin = A3; // choose the input pin (for a pushbutton)
int val = 0; // variable for reading the pin status
void setup()
{
myBarometer.init();
sensors.begin(); // External Temp sensor
lcd.begin (20,4); // initialize the lcd
lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
pinMode(BLpin, INPUT); // declare pushbutton as input
}
void loop()
{
val = digitalRead(BLpin); // test BLpin
if (val == LOW)
{
lcd.backlight(); // Backlight ON and...
sens(); // ... execute "sens"
}
else // if not ...,
{
lcd.noBacklight(); // Backlight OFF and...
sens(); // ...execute "sens"
}
}
void sens() // Main function
{
tmElements_t tm; //DS1307 RTC
sensors.requestTemperatures(); // Send the command to get ext temperature
tempext = sensors.getTempCByIndex(0);
//BARO + TEMP PE I2C
// we read all the values from barometric sensor but use only temperature and pressure
// we might calculate Dew point!
temperature = myBarometer.bmp085GetTemperature(myBarometer.bmp085ReadUT()); //Get the temperature, bmp085ReadUT MUST be called first
pressure = myBarometer.bmp085GetPressure(myBarometer.bmp085ReadUP()); //Get the temperature
altitude = myBarometer.calcAltitude(pressure); //Uncompensated caculation - in Meters
atm = pressure / 101325;
//Hygro sensor DHT-11
int chk = DHT11.read(DHT11PIN);
if (RTC.read(tm))
{
// Hours, minutes, seconds
lcd.setCursor(12, 0);
lcd2digits(tm.Hour);
lcd.print(":");
lcd2digits(tm.Minute);
lcd.print(":");
lcd2digits(tm.Second);
// Only hours and minutes. Uncomment this and comment above
/* lcd.setCursor(14, 0);
lcd2digits(tm.Hour);
lcd.print(":");
lcd2digits(tm.Minute);
*/
lcd.setCursor(0, 0);
lcd.print(tm.Day);
lcd.print('/');
lcd.print(tm.Month);
lcd.print('/');
lcd.print(tmYearToCalendar(tm.Year));
lcd.setCursor(0, 2);
lcd.print("In: ");
lcd.print(temperature); // we can use [lcd.print(int(temperature))] for value without decimals;
lcd.print(" *C "); // otherwise, temp is with two decimals
lcd.setCursor(0, 3);
lcd.print("Ex: ");
lcd.print(tempext); // the same as above ...
lcd.print(" *C ");
lcd.setCursor(0, 1);
lcd.print("P:");
lcd.print(int((pressure) / 100)); // No decimals pressure
lcd.print(" hPa");
// lcd.print(" ");
lcd.setCursor(12, 1);
lcd.print("Hum. ");
lcd.print((int)DHT11.humidity);
lcd.print("%");
} else
{
lcd.clear();
lcd.print("RTC HW ERROR!!!"); // This is for RTC comm error
delay(10000);
}
delay(200);
} // END "sens" function
//LCD nice time function
void lcd2digits(int number) {
if (number >= 0 && number < 10) {
lcd.print('0'); }
lcd.print(number);
}
73 de Adrian, YO3HJV
01 ianuarie 2015
Arduino Energy-meter
A few days ago I was contemplating a little device from a hobby store. It was a smart power gauge meter for tracking the charge and discharge of a battery.
I was wondering if I cannot do it by myself with a Arduino Uno board and some current sensor.
As I'm not a code-freak dude, I did a little research and found a nice start for my project, right here.
I do have a ACS-712 for 5 Amps and also for 30 Amps.
I decided to start with the little one as my project is designated for my portable HF backpack based on ICOM IC-703.
As the Arduino ADC input cannot accept more than 5V, a voltage divider has to be used.
Is the classic one, in wich I used a 10 kOhm from A4 to ground and a 100 kOhm from A4 to the probe. I used SMD resistors but after I measured the ratio I found that the real ratio is not 10 to 1 but somewhere around 10.93:1.
It is important as this ratio will be used to calculate the voltage into the Arduino code.
I modified the code from Instructables because it had some major measurement errors inside as it was written for ACS715 and I do have some problems here with the libraries since I tried to do some tests with I2C LCDs... Also, I found a lot of discussions about how that code is not working properly.
So, below is my version of the code of the Energy meter with ACS-712-05T.
The code is heavily commented so, I believe is easy to understand what is about.
#include <Arduino.h>
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal.h>
/* This sketch describes how to connect a ACS712 - Bidirectional Current Sense Carrier
to the Arduino, and read current flowing through the sensor.
*/
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Easy to connect the LCD Shield
/*
Vcc on carrier board to Arduino +5v
GND on carrier board to Arduino GND
OUT on carrier board to Arduino A0
Insert the power lugs into the loads positive lead circuit,
arrow on carrier board points to load, other lug connects to
power supply positive
*/
int batMonPin = A4; // input pin for the voltage divider
int batVal = 0; // variable for the A/D value
float pinVoltage = 0; // variable to hold the calculated voltage
float batteryVoltage = 0;
int analogInPin = A0; // Analog input pin that the carrier board OUT is connected to
int sensorValue = 0; // value read from the carrier board
int outputValue = 0; // output in milliamps
unsigned long msec = 0;
float time = 0.0;
int sample = 0;
float totalCharge = 0.0;
float averageAmps = 0.0;
float ampSeconds = 0.0;
float ampHours = 0.0;
float wattHours = 0.0;
float amps = 0.0;
void setup()
{
// initialize serial communications at 9600 bps:
Serial.begin(9600);
lcd.begin(20, 4);
}
void loop()
{
int sampleBVal = 0;
int avgBVal = 0;
int sampleAmpVal = 0;
int avgSAV = 0;
for (int x = 0; x < 20; x++) // run through loop 20x
{
// read the analog in value:
sensorValue = analogRead(analogInPin);
sampleAmpVal = sampleAmpVal + sensorValue; // add samples together
batVal = analogRead(batMonPin); // read the voltage on the divider
sampleBVal = sampleBVal + batVal; // add samples together
delay (10); // let ADC settle before next sample
}
avgSAV = sampleAmpVal / 20;
// convert to milli amps
outputValue = (((long)avgSAV * 4980 / 1024) - 2485 ) * 1000 / 130;
/*
Modified by Adrian YO3HJV for real life ACS712-05A
Sensor outputs about 2.485 V at rest.
Analog read produces a value of 0-1023, equating to 0v to 5v.
"((long)sensorValue * 5000 / 1024)" is the voltage on the sensor's output in millivolts.
"5000"mV is ideal value, my board has 4.985 V measured with a precision Voltmeter
Therefore, we have a 2485mv offset to subtract.
The unit produces 185 mv per amp of current, so divide by 0.185 to convert mV to mA.
The documentation said that the ACS has 185mV/Amp but I measured 130 mV/ Amp.
*/
avgBVal = sampleBVal / 20; //divide by 20 (number of samples) to get a steady reading
pinVoltage = (avgBVal * 5.0) / 1024;
// Calculate the voltage on the A/D pin
/* A reading of 1 for the A/D = 0.0048mV
if we multiply the A/D reading by 0.00488 then
we get the voltage on the pin.
It is a good practice to measure the Vcc with a good voltmeter and
to adjust the 5.0 V to the measured value.
Also, depending on wiring and where voltage is being read, under
heavy loads voltage displayed can be well under voltage at supply. monitor
at load or supply and decide.
*/
batteryVoltage = pinVoltage * 10.93; /* 10.93 is the voltage divider ratio
measured with a voltmeter.
First, measure the input voltage (ex. 12V)
Then, measure the voltage at pin A4 (V input).
Then make the ratio between and write it here.
*/
amps = (float) outputValue / 1000;
float watts = amps * batteryVoltage;
//Here we print the data output to serial port.
//Usefull for some data logging onto PC
Serial.print("Volts = " );
Serial.print(batteryVoltage);
Serial.print("\t Current (amps) = ");
Serial.print(amps);
Serial.print("\t Power (Watts) = ");
Serial.print(watts);
sample = sample + 1;
msec = millis();
time = (float) msec / 1000.0;
totalCharge = totalCharge + amps;
averageAmps = totalCharge / sample;
ampSeconds = averageAmps*time;
ampHours = ampSeconds/3600;
wattHours = batteryVoltage * ampHours;
Serial.print("\t Time (hours) = ");
Serial.print(time/3600);
Serial.print("\t Amp Hours (ah) = ");
Serial.print(ampHours);
Serial.print("\t Watt Hours (wh) = ");
Serial.println(wattHours);
lcd.setCursor(0, 0);
lcd.print(batteryVoltage, 2);
lcd.print(" V ");
lcd.setCursor(11, 0);
lcd.print(amps, 2);
lcd.setCursor(16, 0);
lcd.print(" A ");
lcd.setCursor(0, 1);
lcd.print(watts, 2 );
lcd.setCursor(7, 1);
lcd.print(" W ");
lcd.setCursor(11, 1);
lcd.print(time/3600);
lcd.setCursor(16, 1);
lcd.print(" H ");
lcd.setCursor(0, 2);
lcd.print(ampHours, 2);
lcd.print(" Ah ");
lcd.setCursor(11, 2);
lcd.print(wattHours, 2);
lcd.print(" Wh ");
lcd.setCursor(0, 3);
lcd.print("Ch/Dsc: ");
lcd.print(totalCharge, 0);
lcd.print("mA");
// lcd.print(avgBVal);
// wait 10 milliseconds before the next loop
// for the analog-to-digital converter to settle
// after the last reading:
delay(10);
}
//END of void loop ()
I have some plans to develop even further this project... I think is suitable for a smart monitor for my holiday house...
73 de Adrian
I was wondering if I cannot do it by myself with a Arduino Uno board and some current sensor.
As I'm not a code-freak dude, I did a little research and found a nice start for my project, right here.
I do have a ACS-712 for 5 Amps and also for 30 Amps.
I decided to start with the little one as my project is designated for my portable HF backpack based on ICOM IC-703.
As the Arduino ADC input cannot accept more than 5V, a voltage divider has to be used.
Is the classic one, in wich I used a 10 kOhm from A4 to ground and a 100 kOhm from A4 to the probe. I used SMD resistors but after I measured the ratio I found that the real ratio is not 10 to 1 but somewhere around 10.93:1.
It is important as this ratio will be used to calculate the voltage into the Arduino code.
I modified the code from Instructables because it had some major measurement errors inside as it was written for ACS715 and I do have some problems here with the libraries since I tried to do some tests with I2C LCDs... Also, I found a lot of discussions about how that code is not working properly.
So, below is my version of the code of the Energy meter with ACS-712-05T.
The code is heavily commented so, I believe is easy to understand what is about.
#include <Arduino.h>
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal.h>
/* This sketch describes how to connect a ACS712 - Bidirectional Current Sense Carrier
to the Arduino, and read current flowing through the sensor.
*/
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Easy to connect the LCD Shield
/*
Vcc on carrier board to Arduino +5v
GND on carrier board to Arduino GND
OUT on carrier board to Arduino A0
Insert the power lugs into the loads positive lead circuit,
arrow on carrier board points to load, other lug connects to
power supply positive
*/
int batMonPin = A4; // input pin for the voltage divider
int batVal = 0; // variable for the A/D value
float pinVoltage = 0; // variable to hold the calculated voltage
float batteryVoltage = 0;
int analogInPin = A0; // Analog input pin that the carrier board OUT is connected to
int sensorValue = 0; // value read from the carrier board
int outputValue = 0; // output in milliamps
unsigned long msec = 0;
float time = 0.0;
int sample = 0;
float totalCharge = 0.0;
float averageAmps = 0.0;
float ampSeconds = 0.0;
float ampHours = 0.0;
float wattHours = 0.0;
float amps = 0.0;
void setup()
{
// initialize serial communications at 9600 bps:
Serial.begin(9600);
lcd.begin(20, 4);
}
void loop()
{
int sampleBVal = 0;
int avgBVal = 0;
int sampleAmpVal = 0;
int avgSAV = 0;
for (int x = 0; x < 20; x++) // run through loop 20x
{
// read the analog in value:
sensorValue = analogRead(analogInPin);
sampleAmpVal = sampleAmpVal + sensorValue; // add samples together
batVal = analogRead(batMonPin); // read the voltage on the divider
sampleBVal = sampleBVal + batVal; // add samples together
delay (10); // let ADC settle before next sample
}
avgSAV = sampleAmpVal / 20;
// convert to milli amps
outputValue = (((long)avgSAV * 4980 / 1024) - 2485 ) * 1000 / 130;
/*
Modified by Adrian YO3HJV for real life ACS712-05A
Sensor outputs about 2.485 V at rest.
Analog read produces a value of 0-1023, equating to 0v to 5v.
"((long)sensorValue * 5000 / 1024)" is the voltage on the sensor's output in millivolts.
"5000"mV is ideal value, my board has 4.985 V measured with a precision Voltmeter
Therefore, we have a 2485mv offset to subtract.
The unit produces 185 mv per amp of current, so divide by 0.185 to convert mV to mA.
The documentation said that the ACS has 185mV/Amp but I measured 130 mV/ Amp.
*/
avgBVal = sampleBVal / 20; //divide by 20 (number of samples) to get a steady reading
pinVoltage = (avgBVal * 5.0) / 1024;
// Calculate the voltage on the A/D pin
/* A reading of 1 for the A/D = 0.0048mV
if we multiply the A/D reading by 0.00488 then
we get the voltage on the pin.
It is a good practice to measure the Vcc with a good voltmeter and
to adjust the 5.0 V to the measured value.
Also, depending on wiring and where voltage is being read, under
heavy loads voltage displayed can be well under voltage at supply. monitor
at load or supply and decide.
*/
batteryVoltage = pinVoltage * 10.93; /* 10.93 is the voltage divider ratio
measured with a voltmeter.
First, measure the input voltage (ex. 12V)
Then, measure the voltage at pin A4 (V input).
Then make the ratio between and write it here.
*/
amps = (float) outputValue / 1000;
float watts = amps * batteryVoltage;
//Here we print the data output to serial port.
//Usefull for some data logging onto PC
Serial.print("Volts = " );
Serial.print(batteryVoltage);
Serial.print("\t Current (amps) = ");
Serial.print(amps);
Serial.print("\t Power (Watts) = ");
Serial.print(watts);
sample = sample + 1;
msec = millis();
time = (float) msec / 1000.0;
totalCharge = totalCharge + amps;
averageAmps = totalCharge / sample;
ampSeconds = averageAmps*time;
ampHours = ampSeconds/3600;
wattHours = batteryVoltage * ampHours;
Serial.print("\t Time (hours) = ");
Serial.print(time/3600);
Serial.print("\t Amp Hours (ah) = ");
Serial.print(ampHours);
Serial.print("\t Watt Hours (wh) = ");
Serial.println(wattHours);
lcd.setCursor(0, 0);
lcd.print(batteryVoltage, 2);
lcd.print(" V ");
lcd.setCursor(11, 0);
lcd.print(amps, 2);
lcd.setCursor(16, 0);
lcd.print(" A ");
lcd.setCursor(0, 1);
lcd.print(watts, 2 );
lcd.setCursor(7, 1);
lcd.print(" W ");
lcd.setCursor(11, 1);
lcd.print(time/3600);
lcd.setCursor(16, 1);
lcd.print(" H ");
lcd.setCursor(0, 2);
lcd.print(ampHours, 2);
lcd.print(" Ah ");
lcd.setCursor(11, 2);
lcd.print(wattHours, 2);
lcd.print(" Wh ");
lcd.setCursor(0, 3);
lcd.print("Ch/Dsc: ");
lcd.print(totalCharge, 0);
lcd.print("mA");
// lcd.print(avgBVal);
// wait 10 milliseconds before the next loop
// for the analog-to-digital converter to settle
// after the last reading:
delay(10);
}
//END of void loop ()
I have some plans to develop even further this project... I think is suitable for a smart monitor for my holiday house...
73 de Adrian
Abonați-vă la:
Postări (Atom)
Most viewed posts in last 30 days
-
Icom IC-7300 wide band modification by PA2DB: Remove bottom and find the diode matrix (near big chip) Open RX 0.030-74.8Mhz (REMOVE D...
-
Kenwood Programing Software FIRMWARE: KDS-100 504XXXXX 4A46 PTT ID Display feature 176.86 KB CURRENT 401XXXXX F7FC Corrects lock up whe...
-
Icom programming Software GM-110SC rev.1.2. ex1725. IC-3FX 245MHZ THAILAND CB HANDY RADIO, EX2020 REV 1.0. IC-3FGX 80 CHANNELS 245MHZ THAILA...