Bewegende robots worden gewoonlijk gevoed door accu`s of batterijen. Zodra de batterijen minder worden zal de robot allerhande onvoorspelbaar gedrag gaan vertonen. Daarom is het handig als de robot zijn eigen batterijen kan controleren. Dit kan worden bereikt met een accu monitor. Het is bovendien makkelijk in ieder project te implementeren. Wat je nodig hebt heet een spanningsdeler en er zijn slechts 2 weerstandjes nodig om een spanningsdeler te bouwen. Een spanningsdeler wordt voor veel meer dingen gebruikt in robots:
Arduino en ADC |
Hier staan diverse voorbeelden van mogelijke versterkers indien het signaal geen 5Vdc haalt. Four of the differential inputs (ADC1 &ADC0, ADC3 & ADC2, ADC9 & ADC8 and ADC11 & ADC10) are equipped with a programmable gain stage, providing amplification steps of 0 dB (1×), 20 dB (10×) or 46 dB (200×) on the differential input voltage before the ADC conversion.
The ADC minimum current is 0uA the maximum is 1uA as specified in the data sheet Section 28 under input leakage current. In zijn algemeenheid: des te hoger de weerstanden worden gekozen, des te minder invloed dit heeft op de te meten schakeling. Echter als je de weerstanden te hoog kiest dan gaat EMI een rol spelen en zullen de meetresultaten niet erg nauwkeurig zijn. Arduino doet het met 10kΩ. Weerstanden worden verkocht in pakketten. Daar zitten natuurlijk een aantal waardes tussen die niet of nauwelijks in schakelingen worden gebruikt. Deze waardes gebruik ik voor de spanningsdelers indien mogelijk. Een voorbeeld om een accupack te meten is met 2 stuks 68kΩ (blauw grijs oranje).
|
mrBoldermin accumonitor |
mrBoldermin is een mobiel robotje dat gevoed wordt door een accupack van 2 stuks 18650 accu`s. Dit zijn standaard accucellen die in serie geschakeld tezamen maximaal Vin=8,4Vdc leveren. Ik gebruik voor zowel R1 als R2 een weerstand van 47kΩ (geel paars oranje).
Dat de weerstanden gelijk zijn maakt het rekensommetje iets makkelijker want dan wordt het: Vout = Vin * 1/2. Bij een Vout van max 5Vdc bedraagt de meetwaarde 1023 en is de Vin=10Vdc. Per gemeten bit in de ADC stelt dit dus 10/1023 = 0,09775 Vdc voor. De weerstandjes blijken echter niet erg nauwkeurig. Daardoor wijkt de theorie af van de praktijk. We krijgen een meting te zien van (meting is 885) 8.6Vdv terwijl dit 8.3 Vdc moet zijn. Kortom nieuw factor wordt 0,93446327. Dat is een afwijking van ongeveer 4% en dus binnen de toleranties van de weerstanden, maar erg vervelend als je de cellen 4% overlaadt. |
mrBlue accumonitor |
mrBlue is een mobiele robot die wordt gevoed door 2 stuks 18650 accu`s. Dit zijn standaard accucellen die in serie geschakeld tezamen maximaal 8,4Vdc leveren. Als we voor Vmax rekenen met 10Vdc dan hebben we een transferfunctie nodig van H=1/2. Dit betekent dat een spanningsdeler met 2 gelijke weerstandjes nodig is. Ik gebruik 2 weerstandjes van 5k6Ω.
|
Spanningsdeler theorie |
Een spanningsdeler (wiki) is een schakeling die een elektrische spanning in delen splitst. De Arduino`s zijn heel erg goed in het meten van een spanning tussen de 0Vdc - 5Vdc. De µcontroller beschikt daartoe over meerdere Analog to Digital Convertors. Daarom proberen we analoge signalen van sensors vaak om te zetten in een signaal van 0-5Vdc. Een van de makkelijkste vormen van een spanningsdeler is een potentiometer. De beide buitenste pins (uiteinden van de weerstand) van de potmeter worden verbonden met de aarde en met de 5Vdc op de Arduino. Vervolgens wordt de middelste pin (de loper) verbonden met een ADC pin op de Arduino.
An Atmel AT micro controller analogue input pin does not actually measure the pin but merely compares the pin to a preset compare-voltage. This preset compare-voltage can be set to several possibilities with analogReference(). For the Uno R3 the choices are DEFAULT, INTERNAL or EXTERNAL. The DEFAULT setting compares against the Vcc 5Vdc received by the Atmel controller. INTERNAL measures against an internal made 1.1 Vdc. EXTERNAL compares against whatever you connect to pin Vref, but that is no use in this case. The most stable setting in our case is the INTERNAL setting. This means that our 100% measurement (1023) maximum is equal to 1.1 Vdc on the analogue pin. But since we feed our Arduino with 9Vdc which is stabilized on the Arduino board to 5Vdc we can use the DEFAULT option. If you feed through USB it might be more precise to use INTERNAL though. Een meting duurt 13μs - 260μs.
In een vaste spanningsdeler dient de waarde van de weerstandjes zo te worden gekozen dat de spanning op de ADC pin nooit hoger wordt dan 5Vdc. De Arduino zal onherstelbaar beschadigen als de spanning toch hoger wordt. Houdt daarom vooral bij oplaadbare batterijen en accu`s rekening met een ruime marge want tijdens en vlak na het laden kan de spanning aanzienlijk hoger zijn dan de normale werkspanning. Een veilige marge is om te rekenen met 50% boven de normale werkspanning. De mate waarin de spanning moet worden verkleind heet de transfer functie (wiki Eng, wiki NL). Deze functie geeft de verhouding tussen de ingangsspanning van de spanningsdeler en zijn uitgangsspanning. Als we bijvoorbeeld de werkspanning van een 6Vdc accu willen monitoren dan doen we 50% erbij. Dat maakt 9Vdc en voor het rekengemak maken we daar 10Vdc van. Dat is dus dubbel zo hoog als de 5Vdc die we kunnen meten. Daardoor is er een transferfunctie nodig van H=1/2. De wet van Ohm (wiki NL vertelt ons dat we dan 2 identieke weerstandjes nodig hebben. Het stroomverbruik wordt bepaald door beide weerstandjes tezamen. Volgens de datasheet van bijv. de 2560 is het optimaal om een uitgang impedantie van 10kΩ te gebruiken.
Vtyp | Vmax | H | R1Ω+R2Ω | Vmax/R |
6Vdc | 10Vdc | 1/2 | 94kΩ | 63.8µA |
9Vdc | 15Vdc | 1/3 | 147kΩ | 61.2µA |
12Vdc | 20Vdc | 1/4 | 197kΩ | 60,9µA |
|
LDR |
Een vergelijkbare vorm van een spanningsdeler is te vinden bij de LDR.
Dit is de goedkoopste oplossing maar u moet zelf een kleine schakeling maken met een weerstandje van 100kΩ (R2) om een spanningsdeler op te zetten. R1 is de LDR. Vin mag direct aan de voeding (5Vdc) worden aangesloten. Vout moet dan worden aangesloten op de Arduino aan een ingangspin met een AD-convertor. Een LDR kost ongeveer € 0.11/stuk. Adafruit heeft ook een handige uitleg. Er bestaan verschillende types LDR die ieder een eigen maximale weerstand hebben. Indien u absolute waardes nodig heeft dan moeten de waardes dus nog worden geijkt. |
Banggood grondvochtigheid |
Deze sonde verondersteld dat de weerstand tussen 2 pennen in de grond afhankelijk is van de grondvochtigheid. Dus willen we de weerstand over de beide pennen van de sensor meten. We beginnen met een veronderstelde weerstand van de sonde in de grond van rond de 400kΩ. R2 is een vaste weerstand op het breadboard. De waarde van R2 wordt bepaald door de verwachte waarde van R1. Des te lager de waarde van R2 wordt gekozen, des te lager de meetwaardes zullen zijn. Op dit moment testen (grafiekje) we met 2k2Ω (rood, rood, rood) voor R2.
|
//This is an include, so no parsing needed
$qry2 = ("SELECT onderdelen.*,
soortonderdeel.id
FROM onderdelen
JOIN soortonderdeel ON onderdelen.idsrtodd = soortonderdeel.id
WHERE onderdelen.partid = $idpart
;");
$result2 = pg_query($con1, $qry2);
$row4 = pg_fetch_array($result2); //Make the fields accesible
foreach( $row4 AS $key => $val ){
$$key = stripslashes( $val );
}
$jaar = substr($changeddate,0,4);
$maand = substr($changeddate,5,2);
$dag = substr($changeddate,8,2);
$lastupdate = ("$jaar $maand $dag");
$prtsrttxt = get_txt($con1, 26, 1, $idsrtodd);
$jaar = substr($createddate,0,4);
$maand = substr($createddate,5,2);
$dag = substr($createddate,8,2);
$create_stamp = ("$dag $maand $jaar");
//GENERAL INFO FOR EVERY USER **************************************************
echo (" ");
echo (""); //Header
$tmp = get_txt($con1, 9, $koekje[2], 3923); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //If desired
}else{ echo ("$tmp"); } //Else just show if no need to translate
if ($koekje[0]>0 and $koekje[3]>0){ //User is logged in and translates
echo (" (edit)");
}
echo ("Algemene informatie test | ");
if ($codetext) { //Code like KY-026 or NA-41
echo ("");
$tmp = get_txt($con1, 9, $koekje[2], 3739); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //Class for colour
}else{ //So you are not allowed to translate
echo $tmp; //Show screenname of this field in user language
} //End of If you want to translate this fieldname at screen
echo ("  | ");
if ($codelink) {
echo ("$codetext");
}else{
echo ("$codetext");
} //End of if ($pinoutlink)
echo (" | ");
} //End of if ($codetext)
$tmp = get_txt($con1, 9, $koekje[2], 3741); //Fieldname at screen
if ($prijseurinclbtw > 0){ //Show price indication
$prijs_formatted = number_format($prijseurinclbtw / 100, 2, ',', '.');
echo ("$tmp   | ");
echo ("");
if ($urlprijs){
echo ("€  $prijs_formatted");
}else{
echo ("€  $prijs_formatted");
} //End of if ($urlprijs > "")
echo (" | ");
} //End of if ($prijseurinclbtw > 0)
if ($sketchtext) { //Link to OWN SOFTWARE
$tmp = get_txt($con1, 9, $koekje[2], 3738); //Fieldname at screen
echo ("$tmp   | ");
if ($sketchlink) {
echo ("$sketchtext");
}else{
echo ("$sketchtext");
} //End of if ($sketchlink)
echo (" | ");
} //End of if ($sketchtext)
$tmp = get_txt($con1, 9, $koekje[2], 3744); //Fieldname at screen
if ($pinouttext) { //Pinout
echo ("$tmp   | ");
if ($pinoutlink) {
echo ("$pinouttext");
}else{
echo ("$pinouttext");
} //End of if ($pinoutlink)
echo (" | ");
}
$tmp = get_txt($con1, 9, 1, 3735); //Fieldname at screen
$tmp2 = get_txt($con1, 26, 1, $row4[16]); //Group name in its language
echo ("$tmp   | "); //MENU GROUP
echo ("");
echo ("$tmp2 | ");
if ($koekje[0]>0 and $koekje[3]>0){ //User is logged in and translates
$tmp = get_txt($con1, 9, $koekje[2], 3918); //Fieldname at screen LANGUAGE
if ($row4[26] == 1) {$imgname = "nederland.png";}
if ($row4[26] == 2) {$imgname = "deutschland.png";}
if ($row4[26] == 3) {$imgname = "uk.png";}
if ($row4[26] == 4) {$imgname = "france.png";}
echo ("$tmp | ");
echo ("");
echo (" | ");
$tmp = get_txt($con1, 9, $row4[27], 3919); //Fieldname at screen
$statuskey = get_status_key($con1, $status);
$tmp2 = get_txt($con1, 61, $koekje[2], $statuskey); //Fieldname at screen
echo ("$tmp   | "); //STATUS
echo ("$status $tmp2 | ");
}
$tmp = get_txt($con1, 9, 1, 3966); //Fieldname at screen
echo ("$tmp   | "); //LAST CHANGED
$tmp = substr($changeddate, 0, 10);
echo ("$tmp | ");
echo (" ");
return;
////////////////////////////////////////////////////////////////////////////////
function get_status_key($con, $num){
$qry = ("SELECT id FROM status WHERE nr = $num ;");
$result = pg_query($con, $qry);
$row = pg_fetch_array($result);
return ("$row[0]");
}
// LIST ALL SOFTWARE THAT WILL RUN OM THIS PART *****************************
// INCLUDED IN ALL ROBOTS TO CHECK THEIR DIRECTORY
$qry2=("SELECT tblsystext.txtname
, software.urlmain
, software.sftname
, software.sftid
, software.controller
FROM software
LEFT JOIN tblsystext ON software.controller = tblsystext.recordid
WHERE position(' $idpart ' in software.prtlst) > 0
AND software.rstid = 2
AND tblsystext.lanid = $koekje[2]
AND tblsystext.txlid = 19
ORDER BY sftname ASC
, controller ASC
;");
$result = pg_query($con1, $qry2);
if (pg_num_rows($result)>0) { //Only if at least 1 program exists
echo (' ');
echo (""); //Header
$tmp = get_txt($con1, 9, $koekje[2], 3922); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //If desired
}else{ echo ("$tmp"); } //Else just show if no need to translate
echo (" |
---|
");
while($col = pg_fetch_array($result)){
$col[0] = ("$col[0]");
echo (" ");
echo ("$col[2]   | ");
echo ("$col[0] | ");
if ($koekje[0] > 0 and $koekje[3]>0){ //User is logged in and translates
echo ("");
echo ("(Edit) | ");
}
echo (' ');
}
echo (' ');
}
?>
// LIST ALL ROBOTS THAT INCLUDE THIS PART ***********************************
// $idpart
// INCLUDED IN ALL ROBOTS TO CHECK THEIR DIRECTORY
$qry_bpp=("SELECT farmbotversie.id
, farmbotversie.part_list
, tblsystext.txtname
FROM farmbotversie
LEFT JOIN tblsystext ON farmbotversie.id = tblsystext.recordid
WHERE tblsystext.txlid = 27
AND tblsystext.lanid = $koekje[2]
ORDER BY tblsystext.txtname ASC
;");
$result_bpp = pg_query($con1, $qry_bpp);
$header = 0;
while($colbpp = pg_fetch_array($result_bpp)){ //Only if at least 1 robot exists
if(substr_count($colbpp[1], " $idpart ") > 0){
if ($header == 0) {
echo ("");
echo (""); //Header
$tmp = get_txt($con1, 9, $koekje[2], 3879); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //If desired
}else{ echo ("$tmp"); } //Else just show if no need to translate
echo (" | ");
$header++;
} //End of if ($header == 0)
echo ("");
echo ("$colbpp[2]");
//Show software van deze bot
$qry=("SELECT software_basedir
FROM farmbotversie
WHERE id = $colbpp[0];");
$result = pg_query($con1, $qry);
$col = pg_fetch_array ($result ,0);
$dir1 = "/var/www/html/robotigs/software/$col[0]";
$arr1 = glob($dir1, GLOB_ONLYDIR);
if (isset($arr1[0])){
$folder = $arr1[0];
$countertmp = 0;
echo ("");
foreach (scandir($folder, 1) as $filename) {
if ($filename<>"." AND $filename <>".."){
if ($countertmp == 0){
$countertmp++;
} //End of if ($countertmp == 0)
$tmp = "/";
$dir = substr($folder, 14) ."/";
$link = $tmp .$dir .$filename;
$showname = substr($filename, 0 ,-4);
echo ("- ");
echo "$showname";
echo ("
");
} //End of if ($filename<>"." AND $filename <>".."){
} //End of foreach (scandir($folder, 1) as $filename)
echo ("");
} //End of if (isset($arr1[0]))
echo (" | ");
}
}
if ($header > 0){
echo (" ");
}
?>
Warning: include(/var/www/html/robotigs/includes/wishlistperpart.php): Failed to open stream: No such file or directory in /var/www/html/robotigs/includes/parts_header.php on line 128
Warning: include(): Failed opening '/var/www/html/robotigs/includes/wishlistperpart.php' for inclusion (include_path='.:/usr/share/php') in /var/www/html/robotigs/includes/parts_header.php on line 128
| //Location:/robotigs/includes/ picts_show.php
// Requieres: $pict_list and several picture handling javascript routines
//*** FROM HERE ON THE PICTURE CORNER STARTS -----------------------------------
if ($pict_list){ //There are 1 or more pictures to be printed
echo ("");
$counter = 0;
$aantal = str_word_count($pict_list, 0, "0123456789");
$lijst = str_word_count($pict_list, 1, "0123456789");
while($counter < $aantal){
$pictkey = $lijst[$counter];
$qryy1 = ("SELECT *
FROM illustrations
WHERE ill_id = $pictkey
;");
$resulty1 = pg_query($con1, $qryy1);
while($row = pg_fetch_array($resulty1)){ //Fields accesible by their name
foreach( $row AS $key => $val ){
$$key = stripslashes( $val );
}
}
$image = ("/robotigs/documentatie/plaatjes/$ill_name");
$pictfull = str_replace('_small', '', $image); //Pictfull=biggest version
$alt = ("Just wait till your 57....");
$txtname = get_txt($con1, 18, $koekje[2], $ill_id); //Robot illustrations subtitles
echo ("");
echo (' ');
$nummer = $counter + 1;
$txtAfb = get_txt($con1, 9, $koekje[2], 3865); //Pic Afb
if ($koekje[0]>0 and $koekje[3]>0){ //User is logged in and translates
echo ("$txtAfb $nummer: ");
echo ("$txtname");
echo (" ");
echo ("Ontkoppelen ");
if ($counter==0){
echo ("Up ");
}else{
echo ("Up ");
}
if ($nummer == $aantal){
echo ("Down");
}else{
echo ("Down");
}
}else{ //Else of if Meaning the current useer is logged in
echo ("$txtAfb $nummer: $txtname");
}
echo (''); //End of this picture
$counter++; //Prepair for next picture
}
echo (' | '); //All pictures are printed
} //End of if there are 1 or more pictures to be printed ----------------------
?>