https://revspace.nl/api.php?action=feedcontributions&user=Luteijn&feedformat=atomRevSpace - User contributions [en-gb]2024-03-19T10:26:31ZUser contributionsMediaWiki 1.32.1https://revspace.nl/index.php?title=Luteijn/IRRemotes&diff=28308Luteijn/IRRemotes2021-10-30T14:15:09Z<p>Luteijn: </p>
<hr />
<div>===Project "IRRemotes":===<br />
{{Project<br />
|Name=Luteijn/IRRemotes<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=I.R._Baboon_301-1-.png <br />
}}<br />
<br />
Controlling things with an IR Remote is fun. Here's what some of my remotes send out, for easy (re)use in projects with an IR Receiver. Sniffed the codes with a Vortex robot, made up the names in the tables myself.<br />
<br />
==== RC6 'windows' remote ====<br />
This sends 36-bit RC6 codes. They all seem to be of the form 0xC8034x4hl. The leading C doesn't show with my current decoder of choice, or isn't really there. The x is either 8 or 0, flipping for each key pressed, so you can distinguish between a key being held or pushed twice in a row. The h and l indicate the variable bits in the code sent, the tables below give a name to each of these. <br />
<br />
For this first table, the order is a left to right, top to bottom scan of the remote, treating the volume and channel + - as one, rocker, button.<br />
<br />
{| class="wikitable"<br />
|+RC6 'windows'<br />
|-<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label<br />
|-<br />
|0C||power||<br />
|30||pause||<br />
|37||record||<br />
|31||stop||<br />
|29||reverse||<br />
|2C||play<br />
|-<br />
|28||forwards||<br />
|21||previous||<br />
|20||next||<br />
|83||back||<br />
|CB||info||<br />
|58||up<br />
|-<br />
|5A||left||<br />
|5C||ok||<br />
|5B||right||<br />
|59||down||<br />
|10||vol+||<br />
|11||vol-<br />
|-<br />
|5D||logo||<br />
|1E||ch+||<br />
|1F||ch-||<br />
|0D||mute||<br />
|9B||tvrecord||<br />
|3E||tvguide<br />
|-<br />
|2E||tvplay||<br />
|54||disc||<br />
|01||1||<br />
|02||2||<br />
|03||3||<br />
|04||4<br />
|-<br />
|05||5||<br />
|06||6||<br />
|07||7||<br />
|08||8||<br />
|09||9||<br />
|33||*<br />
|-<br />
|00||0||<br />
|32||#||<br />
|3A||clear||<br />
|2F||teletext||<br />
|34||enter||<br />
|6D||red<br />
|-<br />
|6E||green||<br />
|6F||yellow||<br />
|70||blue<br />
|}<br />
<br />
The next table is sorted by in the code order:<br />
{| class="wikitable"<br />
|+RC6 'windows'<br />
|-<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label<br />
|-<br />
|00||0||<br />
|01||1||<br />
|02||2||<br />
|03||3||<br />
|04||4||<br />
|05||5<br />
|-<br />
|06||6||<br />
|07||7||<br />
|08||8||<br />
|09||9||<br />
|0C||power||<br />
|0D||mute<br />
|-<br />
|10||vol+||<br />
|11||vol-||<br />
|1E||ch+||<br />
|1F||ch-||<br />
|20||next||<br />
|21||previous<br />
|-<br />
|28||forwards||<br />
|29||reverse||<br />
|2C||play||<br />
|2E||tvplay||<br />
|2F||teletext||<br />
|30||pause<br />
|-<br />
|31||stop||<br />
|32||#||<br />
|33||*||<br />
|34||enter||<br />
|37||record||<br />
|3A||clear<br />
|-<br />
|3E||tvguide||<br />
|54||disc||<br />
|58||up||<br />
|59||down||<br />
|5A||left||<br />
|5B||right<br />
|-<br />
|5C||ok||<br />
|5D||logo||<br />
|6D||red||<br />
|6E||green||<br />
|6F||yellow||<br />
|70||blue<br />
|-<br />
|83||back||<br />
|9B||tvrecord||<br />
|CB||info<br />
|}<br />
<br />
==== RTeL-Cheapo remote ====<br />
This El-Cheapo remote came with one of my RTL-dongles. Decodes as an NEC-code, sending a '32-bit' code when a key is pressed, followed by a 0-bit code as long as the key is held. Only the lowest 16 bits seem to change. The keypad is a 3x67 matrix<br />
<br />
{| class="wikitable"<br />
|+RTeL-Cheapo<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|FFB24D||power||||FF2AD5||source||||FF6897||mute<br />
|-<br />
|FF32CD||record||||FFA05F||ch+||||FF30CF||time shift<br />
|-<br />
|FF50AF||vol-||||FF02FD||full screen||||FF7887||vol+<br />
|-<br />
|FF48B7||0||||FF40BF||ch-||||FF38C7||recall<br />
|-<br />
|FF906F||1||||FFB847||2||||FFF807||3<br />
|-<br />
|FFB04F||4||||FF9867||5||||FFD827||6<br />
|-<br />
|FF8877||7||||FFA857||8||||FFE817||9<br />
|}<br />
<br />
==== Shinco RC-1606 ====<br />
This remote came with our Chinese DVD player many of the key labels are in Chinese. Decodes as an NEC-code, and like the RTeL-Cheapo remote it will send a '32-bit' code when a key is first pressed, and then a 0-bit code as long as it is held. Only the lowest 16 bits seem to change. The basic layout is 4 buttons per row, but the central bit is 3 rows of 3 slightly larger buttons.<br />
<br />
{| class="wikitable"<br />
|+Shinco RC-160S<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|99728D||screen display||||||||||||||||9938C7||power<br />
|-<br />
|996A95||subtitles||||99CA35||language||||9918E7||channel||||992AD5||point of<br />
|-<br />
|99807F||repeat||||99D02F||pause||||9900FF||play mode||||9942BD||progressive/interlaced<br />
|-<br />
|99B24D||set||||99E01F||title||||9930CF||menu||||9950AF||return<br />
|-<br />
|997887||previous||||993AC5||up||||99DA25||next<br />
|-<br />
|997A85||left||||9928D7||play||||99FA05||right<br />
|-<br />
|99708F||reverse||||99BA45||down||||9968A7||forward<br />
|-<br />
|99F00F||stop||||99C837||pause||||99EA15||step||||99A05F||time search<br />
|-<br />
|99906F||1||||99B847||2||||99F807||3||||99F20D||+5<br />
|-<br />
|99B04F||4||||999867||5||||9948B7||0||||99609F||mute<br />
|-<br />
|9902FD||standard||||9940BF||bookmark||||99E21D||V-||||9912ED||V+<br />
|-<br />
|}<br />
<br />
==== Xtreamer ====<br />
This remote came with my 'Xtreamer'; many of the keys have more than one label, as they are used differently depending on context. Decodes as an NEC-code, and like the RTeL-Cheapo remote it will send a '32-bit' code when a key is first pressed, and then a 0-bit code as long as it is held. Only the lowest 16 bits seem to change. The basic layout is 3 buttons per row. <br />
<br />
{| class="wikitable"<br />
|+Xtreamer<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|FF20DF||power||||||||||FFDA25||home<br />
|-<br />
|FFA25D||1 / add / red||||FFE817||1 / eject / green||||FF48B7||3 / delete / yellow<br />
|-<br />
|FFF20D||4 / zoom||||FFC03F||5 / goto / blue||||FF906F||6 / menu<br />
|-<br />
|FF38C7||7 / setup||||FF4CB3||8 / func||||FF9867||9 / tvout<br />
|-<br />
|FF5AA5||info||||FF8C73||0 / preview||||FF50AF||return<br />
|-<br />
|||||||FF728D||up||||||<br />
|-<br />
|FF30CF||left||||FF609F||enter||||FFA05F||right<br />
|-<br />
|||||||FFB24D||down||||||<br />
|-<br />
|FF1AE5||previous / pgup||||||||||FF0AF5||next / pgdn<br />
|-<br />
|FF32CD||pause/play||||||||||FF7887||stop<br />
|-<br />
|FF3AC5||reverse||||FFD22D||forwards||||FF18E7||vol+<br />
|-<br />
|FF708F||audio||||FF00FF||A-B||||FF827D||vol-<br />
|-<br />
|FFCC33||shuffle||||FF08F7||repeat||||FF58A7||mute<br />
|-<br />
|FFF00F||subtitle||||FF807F||sync subtitle <<||||FF12ED||sync subtitle >><br />
|}<br />
<br />
==== Amoi RC305T ====<br />
This remote came with my 37 inch LCD tv; some of the keys have more than one label, as they are used differently depending on context. Decodes as an NEC-code, and like the RTeL-Cheapo remote it will send a '32-bit' code when a key is first pressed, and then a 0-bit code as long as it is held. Only the lowest 16 bits seem to change. The basic layout is 4 buttons per row. Some of the number keys are not responding well anymore, graphite pads seem to bee depleted. with the remote opened, they could be activated, and it was also clear there is an extra row of keys that have no external buttons connected to them. controlelr chip of the remote is a PT2222, datasheet at http://www.datasheetcatalog.com/datasheets_pdf/P/T/2/2/PT2222-001.shtml tells us this is pin-to-pin compatible with NEC μPD6122.<br />
<br />
{| class="wikitable"<br />
|+Amoi RC305T<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
| || |||| || |||| || ||||40BD02FD||power<br />
|-<br />
|40BD708F||source|||| 40BD728D||sub source||||40BDF20D||last source||||40BD827D||mute<br />
|-<br />
|40BDC23D||freeze||||40BD8A75||aspect||||40BD629D||sleep||||40BD50AF||display<br />
|-<br />
|40BD6A95||video off||||40BDAA55||swap||||40BD926D||pip/pbp||||40BD2AD5||pop<br />
|-<br />
|40BD42BD||picture||||40BDE21D||sound|||| 40BDEA15||headphone -||||40BD1AE5||headphone +<br />
|-<br />
|40BD807F||1 / symbol||||40BD40BF||2 / abc||||40BDC03F||3 / def||||40BDD02F||dual<br />
|-<br />
|40BD20DF||4 / ghi||||40BDA05F||5 / jkl||||40BD609F||6 / mno||||40BDB04F||swap<br />
|-<br />
|40BDE01F||7 / pqrs||||40BD10EF||8 / tuv||||40BD906F||9 / wxyz||||40BD00FF||0 / space<br />
|-<br />
|40BDE817||volume +||||40BD30CF||list||||40BD22DD||page >>||||40BD6897||ch +<br />
|-<br />
|40BDA857||volume -||||40BD28D7||menu||||40BDA25D||page <<||||40BD18E7||ch -<br />
|-<br />
|||||||40BDC837||up / teletext next subpage||||||||||||<br />
|-<br />
|40BD08F7||left||||40BD9867||ok / teletext info||||40BD48B7||right||||||<br />
|-<br />
|||||||40BD8877||down / teletext previous subpage||||||||||||<br />
|-<br />
|40BD12ED||play / pause / teletext x||||40BDB24D||stop / teletext zoom upper||||40BD52AD||digest / teletext zoom lower||||40BDF00F||exit<br />
|-<br />
|40BD9A65||zoom / teletext no zoom||||40BDD22D||rotate / teletext hold||||40BD32CD||function / teletext reveal|||| 40BD0AF5||txt / mix / teletext<br />
|-<br />
|40BD4AB5||red||||40BDCA35||green||||40BD5AA5||yellow||||40BDDA25||blue<br />
|-<br />
|40BD3AC5||no button 1||||40BDBA45||no button 2||||40BD7A85||no button 3||||40BDFA05||no button 4<br />
|}<br />
==== Onkyo RC-342M ====<br />
This remote came with an Onkyo Receiver. Has a switch to switch (some) of the keys to a sub-room setting, and ten programmable keys to try to help to reduce remote-clutter. This also decodes as NEC, and sends a 32-bit code for the first press, and then 0-bit codes to signal the same key is still down. As usual only the lower 4 bits seem to be variable.<br />
<br />
{| class="wikitable"<br />
|+Onkyo RC-342M (WIP)<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|4BB620DF||power||||4BB6BA45||sleep||||4BB6A956||dimmer||||4BB69A65||main||||4BB65AA5||remote<br />
|-<br />
|4BB6F00F||video-1||||4BB6708F||video-2||||4BB6B04F||video-3||||4BB6F906||video-4||||4B3620DF||multi-ch input<br />
|-<br />
<br />
|}<br />
<br />
==== CL-200T ====<br />
This remote came with our motorized curtain rails. This decodes as 'unknown'. it has 8 'channel' buttons, that only send a code once, even if held down. Seems to be a kind of 'attention, next messages are for you'-function, as the actual codes sent by the 'action' button don't seem to vary. There is an open, close and a pair of stop buttons. The stop buttons spam the same command when held, as a kind of panic mode :). <br />
<br />
Code seems 12 bits of code, e.g.:<br />
Raw (24): 400 -1250 400 -1250 400 -1300 1200 -450 400 -1250 450 -1250 1200 -450 400 -1300 400 -1250 400 -1250 400 -1300 400<br />
<br />
____-___-___-___---_-___-___---_-___-___-___-___-____<br />
0 0 0 1 0 0 1 0 0 0 0 0<br />
<br />
Raw (24): 400 -1250 450 -1250 400 -1250 1250 -400 450 -1250 400 -1250 400 -1250 1250 -450 400 -1250 450 -1250 400 -1250 400<br />
____-___-___-___---_-___-___-___---_-___-___-___-____<br />
0 0 0 1 0 0 0 1 0 0 0 0<br />
<br />
Raw (24): 400 -1250 400 -1300 400 -1250 400 -1250 1250 -450 400 -1250 1250 -400 450 -1250 400 -1250 400 -1250 450 -1250 400<br />
____-___-___-___-___---_-___---_-___-___-___-___-____<br />
0 0 0 0 1 0 1 0 0 0 0 0<br />
<br />
Some of this could be lead in and lead out...<br />
<br />
{| class="wikitable"<br />
|+CL-200T(WIP)<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
<br />
|}<br />
<br />
==== Philips RC 240 ====<br />
An RC5 Remote of unknown origin, likely scavenged from trash. Decodes as RC5, 12 bits. MSB is the repeat toggle (so 47f and c7f are the same key). This remote seems funny as it has a bit of state, there are 6 device selection buttons that send their own code, but also change the code send by some of the other keys. The table only lists the codes without the highest bit set.<br />
<br />
{| class="wikitable"<br />
|+Philips RC 240<br />
|-<br />
! !!Tuner!!CD!!TAPE!!TV!!CDV!!VCR<br />
|-<br />
!Label!!Code!!Code!!Code!!Code!!Code!!Code<br />
|-<br />
|Device||47F||53F||4BF||3F||33F||17F<br />
|-<br />
|0||440||500||480||0||300||140<br />
|-<br />
|1||441||501||481||1||301||141<br />
|-<br />
|...||...||...||...||...||...<br />
|-<br />
|9||449||409||489||9||309||149<br />
|-<br />
|memo||469||529||4A9||F||321||14F<br />
|-<br />
|clear||44A||53A||4AB||A||33A||14A<br />
|-<br />
|A.SCAN ←<br />
|-<br />
|MODE →<br />
|-<br />
|P.P.<br />
|-<br />
|PRESET ↑ / RED<br />
|-<br />
|PRESET ↓ / YELLOW<br />
|-<br />
|DISC ↑ / GREEN<br />
|-<br />
|DISC ↓ / BLUE<br />
|-<br />
|VOL. ↑<br />
|-<br />
|VOL. ↓<br />
|-<br />
|STOP<br />
|-<br />
|PAUSE<br />
|-<br />
|PLAY<br />
|-<br />
|←→<br />
|-<br />
|←←<br />
|-<br />
|→→<br />
|-<br />
|TXT<br />
|-<br />
|REC<br />
|-<br />
|STBY<br />
|-<br />
|}<br />
<br />
==== Tt buddy v300 ====<br />
<br />
Remote looks and feels very much like the RTeLCheapo thing, just different buttons are made active/present. Codes are similar with some overlap:<br />
repeat presses are 0bits/FFFFFFFF again, as per the ElCheapo Remote<br />
<br />
{| class="wikitable"<br />
|+Tt buddy v300<br />
|-<br />
!Label!!Code<br />
|-<br />
|power||FF906F<br />
|-<br />
|play/pause||FFB847<br />
|-<br />
|M ||FFB04F<br />
|-<br />
|F-||FF9867<br />
|-<br />
|F+||FFA857<br />
|-<br />
|O-||FFE817<br />
|-<br />
|O+||FFB24D<br />
|-<br />
|T-||FF02FD<br />
|-<br />
|T+||FF00FF<br />
|-<br />
|B-||FF50AF<br />
|-<br />
|B+||FF58A7<br />
|-<br />
|}</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/IRRemotes&diff=28307Luteijn/IRRemotes2021-10-30T13:59:32Z<p>Luteijn: /* Philips RC 240 */</p>
<hr />
<div>===Project "IRRemotes":===<br />
{{Project<br />
|Name=Luteijn/IRRemotes<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=I.R._Baboon_301-1-.png <br />
}}<br />
<br />
Controlling things with an IR Remote is fun. Here's what some of my remotes send out, for easy (re)use in projects with an IR Receiver. Sniffed the codes with a Vortex robot, made up the names in the tables myself.<br />
<br />
==== RC6 'windows' remote ====<br />
This sends 36-bit RC6 codes. They all seem to be of the form 0xC8034x4hl. The leading C doesn't show with my current decoder of choice, or isn't really there. The x is either 8 or 0, flipping for each key pressed, so you can distinguish between a key being held or pushed twice in a row. The h and l indicate the variable bits in the code sent, the tables below give a name to each of these. <br />
<br />
For this first table, the order is a left to right, top to bottom scan of the remote, treating the volume and channel + - as one, rocker, button.<br />
<br />
{| class="wikitable"<br />
|+RC6 'windows'<br />
|-<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label<br />
|-<br />
|0C||power||<br />
|30||pause||<br />
|37||record||<br />
|31||stop||<br />
|29||reverse||<br />
|2C||play<br />
|-<br />
|28||forwards||<br />
|21||previous||<br />
|20||next||<br />
|83||back||<br />
|CB||info||<br />
|58||up<br />
|-<br />
|5A||left||<br />
|5C||ok||<br />
|5B||right||<br />
|59||down||<br />
|10||vol+||<br />
|11||vol-<br />
|-<br />
|5D||logo||<br />
|1E||ch+||<br />
|1F||ch-||<br />
|0D||mute||<br />
|9B||tvrecord||<br />
|3E||tvguide<br />
|-<br />
|2E||tvplay||<br />
|54||disc||<br />
|01||1||<br />
|02||2||<br />
|03||3||<br />
|04||4<br />
|-<br />
|05||5||<br />
|06||6||<br />
|07||7||<br />
|08||8||<br />
|09||9||<br />
|33||*<br />
|-<br />
|00||0||<br />
|32||#||<br />
|3A||clear||<br />
|2F||teletext||<br />
|34||enter||<br />
|6D||red<br />
|-<br />
|6E||green||<br />
|6F||yellow||<br />
|70||blue<br />
|}<br />
<br />
The next table is sorted by in the code order:<br />
{| class="wikitable"<br />
|+RC6 'windows'<br />
|-<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label<br />
|-<br />
|00||0||<br />
|01||1||<br />
|02||2||<br />
|03||3||<br />
|04||4||<br />
|05||5<br />
|-<br />
|06||6||<br />
|07||7||<br />
|08||8||<br />
|09||9||<br />
|0C||power||<br />
|0D||mute<br />
|-<br />
|10||vol+||<br />
|11||vol-||<br />
|1E||ch+||<br />
|1F||ch-||<br />
|20||next||<br />
|21||previous<br />
|-<br />
|28||forwards||<br />
|29||reverse||<br />
|2C||play||<br />
|2E||tvplay||<br />
|2F||teletext||<br />
|30||pause<br />
|-<br />
|31||stop||<br />
|32||#||<br />
|33||*||<br />
|34||enter||<br />
|37||record||<br />
|3A||clear<br />
|-<br />
|3E||tvguide||<br />
|54||disc||<br />
|58||up||<br />
|59||down||<br />
|5A||left||<br />
|5B||right<br />
|-<br />
|5C||ok||<br />
|5D||logo||<br />
|6D||red||<br />
|6E||green||<br />
|6F||yellow||<br />
|70||blue<br />
|-<br />
|83||back||<br />
|9B||tvrecord||<br />
|CB||info<br />
|}<br />
<br />
==== RTeL-Cheapo remote ====<br />
This El-Cheapo remote came with one of my RTL-dongles. Decodes as an NEC-code, sending a '32-bit' code when a key is pressed, followed by a 0-bit code as long as the key is held. Only the lowest 16 bits seem to change. The keypad is a 3x67 matrix<br />
<br />
{| class="wikitable"<br />
|+RTeL-Cheapo<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|FFB24D||power||||FF2AD5||source||||FF6897||mute<br />
|-<br />
|FF32CD||record||||FFA05F||ch+||||FF30CF||time shift<br />
|-<br />
|FF50AF||vol-||||FF02FD||full screen||||FF7887||vol+<br />
|-<br />
|FF48B7||0||||FF40BF||ch-||||FF38C7||recall<br />
|-<br />
|FF906F||1||||FFB847||2||||FFF807||3<br />
|-<br />
|FFB04F||4||||FF9867||5||||FFD827||6<br />
|-<br />
|FF8877||7||||FFA857||8||||FFE817||9<br />
|}<br />
<br />
==== Shinco RC-1606 ====<br />
This remote came with our Chinese DVD player many of the key labels are in Chinese. Decodes as an NEC-code, and like the RTeL-Cheapo remote it will send a '32-bit' code when a key is first pressed, and then a 0-bit code as long as it is held. Only the lowest 16 bits seem to change. The basic layout is 4 buttons per row, but the central bit is 3 rows of 3 slightly larger buttons.<br />
<br />
{| class="wikitable"<br />
|+Shinco RC-160S<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|99728D||screen display||||||||||||||||9938C7||power<br />
|-<br />
|996A95||subtitles||||99CA35||language||||9918E7||channel||||992AD5||point of<br />
|-<br />
|99807F||repeat||||99D02F||pause||||9900FF||play mode||||9942BD||progressive/interlaced<br />
|-<br />
|99B24D||set||||99E01F||title||||9930CF||menu||||9950AF||return<br />
|-<br />
|997887||previous||||993AC5||up||||99DA25||next<br />
|-<br />
|997A85||left||||9928D7||play||||99FA05||right<br />
|-<br />
|99708F||reverse||||99BA45||down||||9968A7||forward<br />
|-<br />
|99F00F||stop||||99C837||pause||||99EA15||step||||99A05F||time search<br />
|-<br />
|99906F||1||||99B847||2||||99F807||3||||99F20D||+5<br />
|-<br />
|99B04F||4||||999867||5||||9948B7||0||||99609F||mute<br />
|-<br />
|9902FD||standard||||9940BF||bookmark||||99E21D||V-||||9912ED||V+<br />
|-<br />
|}<br />
<br />
==== Xtreamer ====<br />
This remote came with my 'Xtreamer'; many of the keys have more than one label, as they are used differently depending on context. Decodes as an NEC-code, and like the RTeL-Cheapo remote it will send a '32-bit' code when a key is first pressed, and then a 0-bit code as long as it is held. Only the lowest 16 bits seem to change. The basic layout is 3 buttons per row. <br />
<br />
{| class="wikitable"<br />
|+Xtreamer<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|FF20DF||power||||||||||FFDA25||home<br />
|-<br />
|FFA25D||1 / add / red||||FFE817||1 / eject / green||||FF48B7||3 / delete / yellow<br />
|-<br />
|FFF20D||4 / zoom||||FFC03F||5 / goto / blue||||FF906F||6 / menu<br />
|-<br />
|FF38C7||7 / setup||||FF4CB3||8 / func||||FF9867||9 / tvout<br />
|-<br />
|FF5AA5||info||||FF8C73||0 / preview||||FF50AF||return<br />
|-<br />
|||||||FF728D||up||||||<br />
|-<br />
|FF30CF||left||||FF609F||enter||||FFA05F||right<br />
|-<br />
|||||||FFB24D||down||||||<br />
|-<br />
|FF1AE5||previous / pgup||||||||||FF0AF5||next / pgdn<br />
|-<br />
|FF32CD||pause/play||||||||||FF7887||stop<br />
|-<br />
|FF3AC5||reverse||||FFD22D||forwards||||FF18E7||vol+<br />
|-<br />
|FF708F||audio||||FF00FF||A-B||||FF827D||vol-<br />
|-<br />
|FFCC33||shuffle||||FF08F7||repeat||||FF58A7||mute<br />
|-<br />
|FFF00F||subtitle||||FF807F||sync subtitle <<||||FF12ED||sync subtitle >><br />
|}<br />
<br />
==== Amoi RC305T ====<br />
This remote came with my 37 inch LCD tv; some of the keys have more than one label, as they are used differently depending on context. Decodes as an NEC-code, and like the RTeL-Cheapo remote it will send a '32-bit' code when a key is first pressed, and then a 0-bit code as long as it is held. Only the lowest 16 bits seem to change. The basic layout is 4 buttons per row. Some of the number keys are not responding well anymore, graphite pads seem to bee depleted. with the remote opened, they could be activated, and it was also clear there is an extra row of keys that have no external buttons connected to them. controlelr chip of the remote is a PT2222, datasheet at http://www.datasheetcatalog.com/datasheets_pdf/P/T/2/2/PT2222-001.shtml tells us this is pin-to-pin compatible with NEC μPD6122.<br />
<br />
{| class="wikitable"<br />
|+Amoi RC305T<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
| || |||| || |||| || ||||40BD02FD||power<br />
|-<br />
|40BD708F||source|||| 40BD728D||sub source||||40BDF20D||last source||||40BD827D||mute<br />
|-<br />
|40BDC23D||freeze||||40BD8A75||aspect||||40BD629D||sleep||||40BD50AF||display<br />
|-<br />
|40BD6A95||video off||||40BDAA55||swap||||40BD926D||pip/pbp||||40BD2AD5||pop<br />
|-<br />
|40BD42BD||picture||||40BDE21D||sound|||| 40BDEA15||headphone -||||40BD1AE5||headphone +<br />
|-<br />
|40BD807F||1 / symbol||||40BD40BF||2 / abc||||40BDC03F||3 / def||||40BDD02F||dual<br />
|-<br />
|40BD20DF||4 / ghi||||40BDA05F||5 / jkl||||40BD609F||6 / mno||||40BDB04F||swap<br />
|-<br />
|40BDE01F||7 / pqrs||||40BD10EF||8 / tuv||||40BD906F||9 / wxyz||||40BD00FF||0 / space<br />
|-<br />
|40BDE817||volume +||||40BD30CF||list||||40BD22DD||page >>||||40BD6897||ch +<br />
|-<br />
|40BDA857||volume -||||40BD28D7||menu||||40BDA25D||page <<||||40BD18E7||ch -<br />
|-<br />
|||||||40BDC837||up / teletext next subpage||||||||||||<br />
|-<br />
|40BD08F7||left||||40BD9867||ok / teletext info||||40BD48B7||right||||||<br />
|-<br />
|||||||40BD8877||down / teletext previous subpage||||||||||||<br />
|-<br />
|40BD12ED||play / pause / teletext x||||40BDB24D||stop / teletext zoom upper||||40BD52AD||digest / teletext zoom lower||||40BDF00F||exit<br />
|-<br />
|40BD9A65||zoom / teletext no zoom||||40BDD22D||rotate / teletext hold||||40BD32CD||function / teletext reveal|||| 40BD0AF5||txt / mix / teletext<br />
|-<br />
|40BD4AB5||red||||40BDCA35||green||||40BD5AA5||yellow||||40BDDA25||blue<br />
|-<br />
|40BD3AC5||no button 1||||40BDBA45||no button 2||||40BD7A85||no button 3||||40BDFA05||no button 4<br />
|}<br />
==== Onkyo RC-342M ====<br />
This remote came with an Onkyo Receiver. Has a switch to switch (some) of the keys to a sub-room setting, and ten programmable keys to try to help to reduce remote-clutter. This also decodes as NEC, and sends a 32-bit code for the first press, and then 0-bit codes to signal the same key is still down. As usual only the lower 4 bits seem to be variable.<br />
<br />
{| class="wikitable"<br />
|+Onkyo RC-342M (WIP)<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|4BB620DF||power||||4BB6BA45||sleep||||4BB6A956||dimmer||||4BB69A65||main||||4BB65AA5||remote<br />
|-<br />
|4BB6F00F||video-1||||4BB6708F||video-2||||4BB6B04F||video-3||||4BB6F906||video-4||||4B3620DF||multi-ch input<br />
|-<br />
<br />
|}<br />
<br />
==== CL-200T ====<br />
This remote came with our motorized curtain rails. This decodes as 'unknown'. it has 8 'channel' buttons, that only send a code once, even if held down. Seems to be a kind of 'attention, next messages are for you'-function, as the actual codes sent by the 'action' button don't seem to vary. There is an open, close and a pair of stop buttons. The stop buttons spam the same command when held, as a kind of panic mode :). <br />
<br />
Code seems 12 bits of code, e.g.:<br />
Raw (24): 400 -1250 400 -1250 400 -1300 1200 -450 400 -1250 450 -1250 1200 -450 400 -1300 400 -1250 400 -1250 400 -1300 400<br />
<br />
____-___-___-___---_-___-___---_-___-___-___-___-____<br />
0 0 0 1 0 0 1 0 0 0 0 0<br />
<br />
Raw (24): 400 -1250 450 -1250 400 -1250 1250 -400 450 -1250 400 -1250 400 -1250 1250 -450 400 -1250 450 -1250 400 -1250 400<br />
____-___-___-___---_-___-___-___---_-___-___-___-____<br />
0 0 0 1 0 0 0 1 0 0 0 0<br />
<br />
Raw (24): 400 -1250 400 -1300 400 -1250 400 -1250 1250 -450 400 -1250 1250 -400 450 -1250 400 -1250 400 -1250 450 -1250 400<br />
____-___-___-___-___---_-___---_-___-___-___-___-____<br />
0 0 0 0 1 0 1 0 0 0 0 0<br />
<br />
Some of this could be lead in and lead out...<br />
<br />
{| class="wikitable"<br />
|+CL-200T(WIP)<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
<br />
|}<br />
<br />
==== Philips RC 240 ====<br />
An RC5 Remote of unknown origin, likely scavenged from trash. Decodes as RC5, 12 bits. MSB is the repeat toggle (so 47f and c7f are the same key). This remote seems funny as it has a bit of state, there are 6 device selection buttons that send their own code, but also change the code send by some of the other keys. The table only lists the codes without the highest bit set.<br />
<br />
{| class="wikitable"<br />
|+Philips RC 240<br />
|-<br />
! !!Tuner!!CD!!TAPE!!TV!!CDV!!VCR<br />
|-<br />
!Label!!Code!!Code!!Code!!Code!!Code!!Code<br />
|-<br />
|Device||47F||53F||4BF||3F||33F||17F<br />
|-<br />
|0||440||500||480||0||300||140<br />
|-<br />
|1||441||501||481||1||301||141<br />
|-<br />
|...||...||...||...||...||...<br />
|-<br />
|9||449||409||489||9||309||149<br />
|-<br />
|memo||469||529||4A9||F||321||14F<br />
|-<br />
|clear||44A||53A||4AB||A||33A||14A<br />
|-<br />
|A.SCAN ←<br />
|-<br />
|MODE →<br />
|-<br />
|P.P.<br />
|-<br />
|PRESET ↑ / RED<br />
|-<br />
|PRESET ↓ / YELLOW<br />
|-<br />
|DISC ↑ / GREEN<br />
|-<br />
|DISC ↓ / BLUE<br />
|-<br />
|VOL. ↑<br />
|-<br />
|VOL. ↓<br />
|-<br />
|STOP<br />
|-<br />
|PAUSE<br />
|-<br />
|PLAY<br />
|-<br />
|←→<br />
|-<br />
|←←<br />
|-<br />
|→→<br />
|-<br />
|TXT<br />
|-<br />
|REC<br />
|-<br />
|STBY<br />
|-<br />
}<br />
<br />
==== Tt buddy v300 ====<br />
<br />
Remote looks and feels very much like the RTeLCheapo thing. Codes are similar with some overlap:</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/IRRemotes&diff=28306Luteijn/IRRemotes2021-10-30T13:58:31Z<p>Luteijn: /* Philips RC 240 */</p>
<hr />
<div>===Project "IRRemotes":===<br />
{{Project<br />
|Name=Luteijn/IRRemotes<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=I.R._Baboon_301-1-.png <br />
}}<br />
<br />
Controlling things with an IR Remote is fun. Here's what some of my remotes send out, for easy (re)use in projects with an IR Receiver. Sniffed the codes with a Vortex robot, made up the names in the tables myself.<br />
<br />
==== RC6 'windows' remote ====<br />
This sends 36-bit RC6 codes. They all seem to be of the form 0xC8034x4hl. The leading C doesn't show with my current decoder of choice, or isn't really there. The x is either 8 or 0, flipping for each key pressed, so you can distinguish between a key being held or pushed twice in a row. The h and l indicate the variable bits in the code sent, the tables below give a name to each of these. <br />
<br />
For this first table, the order is a left to right, top to bottom scan of the remote, treating the volume and channel + - as one, rocker, button.<br />
<br />
{| class="wikitable"<br />
|+RC6 'windows'<br />
|-<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label<br />
|-<br />
|0C||power||<br />
|30||pause||<br />
|37||record||<br />
|31||stop||<br />
|29||reverse||<br />
|2C||play<br />
|-<br />
|28||forwards||<br />
|21||previous||<br />
|20||next||<br />
|83||back||<br />
|CB||info||<br />
|58||up<br />
|-<br />
|5A||left||<br />
|5C||ok||<br />
|5B||right||<br />
|59||down||<br />
|10||vol+||<br />
|11||vol-<br />
|-<br />
|5D||logo||<br />
|1E||ch+||<br />
|1F||ch-||<br />
|0D||mute||<br />
|9B||tvrecord||<br />
|3E||tvguide<br />
|-<br />
|2E||tvplay||<br />
|54||disc||<br />
|01||1||<br />
|02||2||<br />
|03||3||<br />
|04||4<br />
|-<br />
|05||5||<br />
|06||6||<br />
|07||7||<br />
|08||8||<br />
|09||9||<br />
|33||*<br />
|-<br />
|00||0||<br />
|32||#||<br />
|3A||clear||<br />
|2F||teletext||<br />
|34||enter||<br />
|6D||red<br />
|-<br />
|6E||green||<br />
|6F||yellow||<br />
|70||blue<br />
|}<br />
<br />
The next table is sorted by in the code order:<br />
{| class="wikitable"<br />
|+RC6 'windows'<br />
|-<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label!!<br />
!Code!!Label<br />
|-<br />
|00||0||<br />
|01||1||<br />
|02||2||<br />
|03||3||<br />
|04||4||<br />
|05||5<br />
|-<br />
|06||6||<br />
|07||7||<br />
|08||8||<br />
|09||9||<br />
|0C||power||<br />
|0D||mute<br />
|-<br />
|10||vol+||<br />
|11||vol-||<br />
|1E||ch+||<br />
|1F||ch-||<br />
|20||next||<br />
|21||previous<br />
|-<br />
|28||forwards||<br />
|29||reverse||<br />
|2C||play||<br />
|2E||tvplay||<br />
|2F||teletext||<br />
|30||pause<br />
|-<br />
|31||stop||<br />
|32||#||<br />
|33||*||<br />
|34||enter||<br />
|37||record||<br />
|3A||clear<br />
|-<br />
|3E||tvguide||<br />
|54||disc||<br />
|58||up||<br />
|59||down||<br />
|5A||left||<br />
|5B||right<br />
|-<br />
|5C||ok||<br />
|5D||logo||<br />
|6D||red||<br />
|6E||green||<br />
|6F||yellow||<br />
|70||blue<br />
|-<br />
|83||back||<br />
|9B||tvrecord||<br />
|CB||info<br />
|}<br />
<br />
==== RTeL-Cheapo remote ====<br />
This El-Cheapo remote came with one of my RTL-dongles. Decodes as an NEC-code, sending a '32-bit' code when a key is pressed, followed by a 0-bit code as long as the key is held. Only the lowest 16 bits seem to change. The keypad is a 3x67 matrix<br />
<br />
{| class="wikitable"<br />
|+RTeL-Cheapo<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|FFB24D||power||||FF2AD5||source||||FF6897||mute<br />
|-<br />
|FF32CD||record||||FFA05F||ch+||||FF30CF||time shift<br />
|-<br />
|FF50AF||vol-||||FF02FD||full screen||||FF7887||vol+<br />
|-<br />
|FF48B7||0||||FF40BF||ch-||||FF38C7||recall<br />
|-<br />
|FF906F||1||||FFB847||2||||FFF807||3<br />
|-<br />
|FFB04F||4||||FF9867||5||||FFD827||6<br />
|-<br />
|FF8877||7||||FFA857||8||||FFE817||9<br />
|}<br />
<br />
==== Shinco RC-1606 ====<br />
This remote came with our Chinese DVD player many of the key labels are in Chinese. Decodes as an NEC-code, and like the RTeL-Cheapo remote it will send a '32-bit' code when a key is first pressed, and then a 0-bit code as long as it is held. Only the lowest 16 bits seem to change. The basic layout is 4 buttons per row, but the central bit is 3 rows of 3 slightly larger buttons.<br />
<br />
{| class="wikitable"<br />
|+Shinco RC-160S<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|99728D||screen display||||||||||||||||9938C7||power<br />
|-<br />
|996A95||subtitles||||99CA35||language||||9918E7||channel||||992AD5||point of<br />
|-<br />
|99807F||repeat||||99D02F||pause||||9900FF||play mode||||9942BD||progressive/interlaced<br />
|-<br />
|99B24D||set||||99E01F||title||||9930CF||menu||||9950AF||return<br />
|-<br />
|997887||previous||||993AC5||up||||99DA25||next<br />
|-<br />
|997A85||left||||9928D7||play||||99FA05||right<br />
|-<br />
|99708F||reverse||||99BA45||down||||9968A7||forward<br />
|-<br />
|99F00F||stop||||99C837||pause||||99EA15||step||||99A05F||time search<br />
|-<br />
|99906F||1||||99B847||2||||99F807||3||||99F20D||+5<br />
|-<br />
|99B04F||4||||999867||5||||9948B7||0||||99609F||mute<br />
|-<br />
|9902FD||standard||||9940BF||bookmark||||99E21D||V-||||9912ED||V+<br />
|-<br />
|}<br />
<br />
==== Xtreamer ====<br />
This remote came with my 'Xtreamer'; many of the keys have more than one label, as they are used differently depending on context. Decodes as an NEC-code, and like the RTeL-Cheapo remote it will send a '32-bit' code when a key is first pressed, and then a 0-bit code as long as it is held. Only the lowest 16 bits seem to change. The basic layout is 3 buttons per row. <br />
<br />
{| class="wikitable"<br />
|+Xtreamer<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|FF20DF||power||||||||||FFDA25||home<br />
|-<br />
|FFA25D||1 / add / red||||FFE817||1 / eject / green||||FF48B7||3 / delete / yellow<br />
|-<br />
|FFF20D||4 / zoom||||FFC03F||5 / goto / blue||||FF906F||6 / menu<br />
|-<br />
|FF38C7||7 / setup||||FF4CB3||8 / func||||FF9867||9 / tvout<br />
|-<br />
|FF5AA5||info||||FF8C73||0 / preview||||FF50AF||return<br />
|-<br />
|||||||FF728D||up||||||<br />
|-<br />
|FF30CF||left||||FF609F||enter||||FFA05F||right<br />
|-<br />
|||||||FFB24D||down||||||<br />
|-<br />
|FF1AE5||previous / pgup||||||||||FF0AF5||next / pgdn<br />
|-<br />
|FF32CD||pause/play||||||||||FF7887||stop<br />
|-<br />
|FF3AC5||reverse||||FFD22D||forwards||||FF18E7||vol+<br />
|-<br />
|FF708F||audio||||FF00FF||A-B||||FF827D||vol-<br />
|-<br />
|FFCC33||shuffle||||FF08F7||repeat||||FF58A7||mute<br />
|-<br />
|FFF00F||subtitle||||FF807F||sync subtitle <<||||FF12ED||sync subtitle >><br />
|}<br />
<br />
==== Amoi RC305T ====<br />
This remote came with my 37 inch LCD tv; some of the keys have more than one label, as they are used differently depending on context. Decodes as an NEC-code, and like the RTeL-Cheapo remote it will send a '32-bit' code when a key is first pressed, and then a 0-bit code as long as it is held. Only the lowest 16 bits seem to change. The basic layout is 4 buttons per row. Some of the number keys are not responding well anymore, graphite pads seem to bee depleted. with the remote opened, they could be activated, and it was also clear there is an extra row of keys that have no external buttons connected to them. controlelr chip of the remote is a PT2222, datasheet at http://www.datasheetcatalog.com/datasheets_pdf/P/T/2/2/PT2222-001.shtml tells us this is pin-to-pin compatible with NEC μPD6122.<br />
<br />
{| class="wikitable"<br />
|+Amoi RC305T<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
| || |||| || |||| || ||||40BD02FD||power<br />
|-<br />
|40BD708F||source|||| 40BD728D||sub source||||40BDF20D||last source||||40BD827D||mute<br />
|-<br />
|40BDC23D||freeze||||40BD8A75||aspect||||40BD629D||sleep||||40BD50AF||display<br />
|-<br />
|40BD6A95||video off||||40BDAA55||swap||||40BD926D||pip/pbp||||40BD2AD5||pop<br />
|-<br />
|40BD42BD||picture||||40BDE21D||sound|||| 40BDEA15||headphone -||||40BD1AE5||headphone +<br />
|-<br />
|40BD807F||1 / symbol||||40BD40BF||2 / abc||||40BDC03F||3 / def||||40BDD02F||dual<br />
|-<br />
|40BD20DF||4 / ghi||||40BDA05F||5 / jkl||||40BD609F||6 / mno||||40BDB04F||swap<br />
|-<br />
|40BDE01F||7 / pqrs||||40BD10EF||8 / tuv||||40BD906F||9 / wxyz||||40BD00FF||0 / space<br />
|-<br />
|40BDE817||volume +||||40BD30CF||list||||40BD22DD||page >>||||40BD6897||ch +<br />
|-<br />
|40BDA857||volume -||||40BD28D7||menu||||40BDA25D||page <<||||40BD18E7||ch -<br />
|-<br />
|||||||40BDC837||up / teletext next subpage||||||||||||<br />
|-<br />
|40BD08F7||left||||40BD9867||ok / teletext info||||40BD48B7||right||||||<br />
|-<br />
|||||||40BD8877||down / teletext previous subpage||||||||||||<br />
|-<br />
|40BD12ED||play / pause / teletext x||||40BDB24D||stop / teletext zoom upper||||40BD52AD||digest / teletext zoom lower||||40BDF00F||exit<br />
|-<br />
|40BD9A65||zoom / teletext no zoom||||40BDD22D||rotate / teletext hold||||40BD32CD||function / teletext reveal|||| 40BD0AF5||txt / mix / teletext<br />
|-<br />
|40BD4AB5||red||||40BDCA35||green||||40BD5AA5||yellow||||40BDDA25||blue<br />
|-<br />
|40BD3AC5||no button 1||||40BDBA45||no button 2||||40BD7A85||no button 3||||40BDFA05||no button 4<br />
|}<br />
==== Onkyo RC-342M ====<br />
This remote came with an Onkyo Receiver. Has a switch to switch (some) of the keys to a sub-room setting, and ten programmable keys to try to help to reduce remote-clutter. This also decodes as NEC, and sends a 32-bit code for the first press, and then 0-bit codes to signal the same key is still down. As usual only the lower 4 bits seem to be variable.<br />
<br />
{| class="wikitable"<br />
|+Onkyo RC-342M (WIP)<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
|4BB620DF||power||||4BB6BA45||sleep||||4BB6A956||dimmer||||4BB69A65||main||||4BB65AA5||remote<br />
|-<br />
|4BB6F00F||video-1||||4BB6708F||video-2||||4BB6B04F||video-3||||4BB6F906||video-4||||4B3620DF||multi-ch input<br />
|-<br />
<br />
|}<br />
<br />
==== CL-200T ====<br />
This remote came with our motorized curtain rails. This decodes as 'unknown'. it has 8 'channel' buttons, that only send a code once, even if held down. Seems to be a kind of 'attention, next messages are for you'-function, as the actual codes sent by the 'action' button don't seem to vary. There is an open, close and a pair of stop buttons. The stop buttons spam the same command when held, as a kind of panic mode :). <br />
<br />
Code seems 12 bits of code, e.g.:<br />
Raw (24): 400 -1250 400 -1250 400 -1300 1200 -450 400 -1250 450 -1250 1200 -450 400 -1300 400 -1250 400 -1250 400 -1300 400<br />
<br />
____-___-___-___---_-___-___---_-___-___-___-___-____<br />
0 0 0 1 0 0 1 0 0 0 0 0<br />
<br />
Raw (24): 400 -1250 450 -1250 400 -1250 1250 -400 450 -1250 400 -1250 400 -1250 1250 -450 400 -1250 450 -1250 400 -1250 400<br />
____-___-___-___---_-___-___-___---_-___-___-___-____<br />
0 0 0 1 0 0 0 1 0 0 0 0<br />
<br />
Raw (24): 400 -1250 400 -1300 400 -1250 400 -1250 1250 -450 400 -1250 1250 -400 450 -1250 400 -1250 400 -1250 450 -1250 400<br />
____-___-___-___-___---_-___---_-___-___-___-___-____<br />
0 0 0 0 1 0 1 0 0 0 0 0<br />
<br />
Some of this could be lead in and lead out...<br />
<br />
{| class="wikitable"<br />
|+CL-200T(WIP)<br />
|-<br />
!Code!!Label!!!!Code!!Label!!!!Code!!Label!!!!Code!!Label<br />
|-<br />
<br />
|}<br />
<br />
==== Philips RC 240 ====<br />
An RC5 Remote of unknown origin, likely scavenged from trash. Decodes as RC5, 12 bits. MSB is the repeat toggle (so 47f and c7f are the same key). This remote seems funny as it has a bit of state, there are 6 device selection buttons that send their own code, but also change the code send by some of the other keys. The table only lists the codes without the highest bit set.<br />
<br />
<br />
{| class="wikitable"<br />
|+Philips RC 240<br />
|-<br />
! !!Tuner!!CD!!TAPE!!TV!!CDV!!VCR<br />
|-<br />
!Label!!Code!!Code!!Code!!Code!!Code!!Code<br />
|-<br />
|Device||47F||53F||4BF||3F||33F||17F<br />
|-<br />
|0||440||500||480||0||300||140<br />
|-<br />
|1||441||501||481||1||301||141<br />
|-<br />
|...||...||...||...||...||...<br />
|-<br />
|9||449||409||489||9||309||149<br />
|-<br />
|memo||469||529||4A9||F||321||14F<br />
|-<br />
|clear||44A||53A||4AB||A||33A||14A<br />
|-<br />
|A.SCAN ←<br />
|-<br />
|MODE →<br />
|-<br />
|P.P.<br />
|-<br />
|PRESET ↑ / RED<br />
|-<br />
|PRESET ↓ / YELLOW<br />
|-<br />
|DISC ↑ / GREEN<br />
|-<br />
|DISC ↓ / BLUE<br />
|-<br />
|VOL. ↑<br />
|-<br />
|VOL. ↓<br />
|-<br />
|STOP<br />
|-<br />
|PAUSE<br />
|-<br />
|PLAY<br />
|-<br />
|←→<br />
|-<br />
|←←<br />
|-<br />
|→→<br />
|-<br />
|TXT<br />
|-<br />
|REC<br />
|-<br />
|STBY<br />
|-<br />
<br />
==== Tt buddy v300 ====<br />
<br />
Remote looks and feels very much like the RTeLCheapo thing. Codes are similar with some overlap:</div>Luteijnhttps://revspace.nl/index.php?title=Lasercutter3&diff=22354Lasercutter32019-07-24T05:00:31Z<p>Luteijn: /* Materialen en ervaringen */</p>
<hr />
<div>RevSpace heeft een nieuwe lasercutter!<br />
<div style="float: right; clear: right;">__TOC__</div><br />
<br />
= Specificaties =<br />
<br />
* Merk: FabCreator<br />
* Model: FabKit MK5<br />
* Productie: MK5 Batch 2<br />
* Geleverd: vrijdag 22 december 2017<br />
* Laservermogen: 40 W<br />
* Aanpassingen: <br />
** <tt>kill_button_enable false</tt> omdat interlock-bordje onterecht (maar heel kort) triggert; de laser gaat nog wel uit.<br />
** Externe computer ipv Pipo. De pipo is te traag en te onhandig.<br />
** Rode draad van USB tussen externe computer en smoothieboard onderbroken zodat de smoothie uitgaat bij gebruik van noodstop.<br />
* Gewenste aanpassingen:<br />
** Herontwerp interlock-bord<br />
** Noodstop dubbelpolig ipv enkelpolig laten schakelen (3SB3400-0E besteld door Sebastius)<br />
** Noodstop killbutton-functie laten triggeren en dan rode draad in USB-kabel herstellen (3SB3400-0B besteld door Sebastius)<br />
<br />
= Veiligheidswaarschuwingen =<br />
<br />
{|<br />
| [[Image:DIN4844-giftige-stoffen.svg|100px]] ||<br />
; Giftige gassen: Bij het snijden van bepaalde materialen kunnen giftige, zelfs dodelijke, gassen vrijkomen.<br />
|-<br />
| [[Image:DIN4844-omglaz0rs.svg|100px]] ||<br />
; Laserstraling: Kan o.a. blindheid veroorzaken.<br />
|-<br />
| [[Image:DIN4844-brandgevaarlijke-stoffen.svg|100px]] ||<br />
; Brandgevaar: Het te snijden/graveren materiaal kan vlam vatten.<br />
|}<br />
<br />
= Veiligheidsinstructies =<br />
<br />
Je mag de lasercutter alleen bedienen als je aan de volgende eisen voldoet:<br />
* Je hebt '''OPNIEUW''' persoonlijke lasercutter-training gehad bij revspace (Lasercutter3 is erg anders, en duur!)<br />
* Je bent nuchter, wakker en alert<br />
* Je hebt deze wiki-pagina recent gelezen (check regelmatig de wijzigingen!)<br />
* Zie ook: http://hackaday.com/2016/05/31/how-to-fail-at-laser-cutting/<br />
<br />
= Bevoegde Operators =<br />
{| class="wikitable"<br />
|-<br />
! Naam<br />
! Geïnstrueerd door<br />
! Ervaring met software<br />
|-<br />
| Juerd || Fabcreator || LaserWeb en LightBurn<br />
|-<br />
| Sebastius || Fabcreator || LaserWeb en LightBurn<br />
|-<br />
| Petroleus || Juerd || LaserWeb<br />
|-<br />
| Crashjuh || Juerd || LaserWeb<br />
|-<br />
| molenaar || Crashjuh || LaserWeb<br />
|-<br />
| luteijn || Crashjuh || LaserWeb<br />
|-<br />
| pepman || Crashjuh || LaserWeb<br />
|-<br />
| benadski || Crashjuh || LaserWeb<br />
|-<br />
| Pwuts || benadski || LaserWeb<br />
|-<br />
| Roosted || Juerd || LaserWeb<br />
|-<br />
| Trashman || Crashjuh || LaserWeb<br />
|-<br />
| Thomas || Crashjuh || LaserWeb<br />
|-<br />
| pinoaffe || Juerd || LaserWeb<br />
|-<br />
| f0x || Juerd || LaserWeb en LightBurn<br />
|-<br />
| Peetz0r || Juerd || LaserWeb en LightBurn<br />
|-<br />
| Peterbjornx || Crashjuh || LaserWeb<br />
|-<br />
| flok || Crashjuh || LaserWeb<br />
|-<br />
| Foobar || Crashjuh || LaserWeb<br />
|-<br />
| Boekenwuurm || Sebastius || LaserWeb<br />
|-<br />
| Knorrie || Sebastius || LaserWeb<br />
|-<br />
| joepie91 || Juerd || LaserWeb<br />
|-<br />
| WinSCaP || Sebastius || LaserWeb en LightBurn<br />
|-<br />
| Frido || Juerd || LaserWeb<br />
|-<br />
| SelfishPopcorn || Juerd || LaserWeb<br />
|-<br />
| Duco || Crashjuh || LaserWeb<br />
|-<br />
| cmpxchg || Juerd || LaserWeb en LightBurn<br />
|-<br />
| ZentronStar || Crashjuh || LaserWeb<br />
|-<br />
| Semafoor || Juerd || LaserWeb<br />
|-<br />
| AlexanderB || Juerd || LightBurn<br />
|-<br />
| Pepman || Juerd || LightBurn<br />
|-<br />
| Kinroy || Juerd || LightBurn<br />
|-<br />
| Andrey || Juerd || LightBurn<br />
|-<br />
| Kamitor || Sebastius || LaserWeb<br />
|-<br />
| textor || Crashjuh en Andrey || LaserWeb en LightBurn<br />
|-<br />
| lucanator || Juerd || LightBurn<br />
|-<br />
| PeterC || Sebastius || LaserWeb<br />
|-<br />
| ASH || Sebastius || LaserWeb<br />
|-<br />
| alfodr || Sebastius || LaserWeb<br />
|-<br />
| h3x4d3c1m4l || Juerd || LightBurn<br />
|-<br />
| minicom || Juerd || LightBurn<br />
|-<br />
| polyfloyd || Crashjuh || LaserWeb<br />
|-<br />
| rowan8k || Crashjuh || LaserWeb<br />
|-<br />
| Will_1_Am || Juerd || LightBurn<br />
|-<br />
| Sonja || Juerd || LightBurn<br />
|-<br />
| WoLFJuh || Juerd || LightBurn<br />
|-<br />
| Gori || Juerd || LightBurn<br />
|-<br />
| Maxell || Crashjuh || LaserWeb<br />
|-<br />
| Atoomnet || Juerd || LightBurn<br />
|-<br />
| Tempestas || Juerd || LightBurn<br />
|-<br />
| Jelle || Juerd || LightBurn<br />
|-<br />
| sttc || Juerd || LightBurn<br />
|-<br />
| Kartoffel || Crashjuh || LaserWeb<br />
|-<br />
| Skywalker11 || Juerd || LightBurn<br />
|-<br />
| Athunc || Juerd || LightBurn<br />
|-<br />
|}<br />
<br />
= Gebruik =<br />
<br />
== Voor ==<br />
# Ruim op wat je voorganger achter heeft gelaten<br />
# Schakel in:<br />
#* Schakelaar op contactdoos (chiller zal piepen)<br />
#* Noodstop op lasercutter: draaien<br />
#* Laser: sleutel draaien, indicatorlampje wordt groen.<br />
#* Laptop<br />
# Controleer:<br />
#* Locatie CO2-brandblusser<br />
#* Werking afzuiging: voel of er lucht afgezogen wordt<br />
#* Werking compressor: voel of er lucht ingeblazen wordt bij de snijkop (gaat pas aan als ...)<br />
#* Werking chiller: temperatuur moet dalen naar rond de 16 a 17 graden, ALARM-lampje moet uit zijn<br />
#* Of de spiegels schoon zijn. Zo niet: melding maken, lasercutter niet gebruiken. Niet zelf schoonmaken!<br />
#* Of de bodem van de machine niet helemaal vol ligt met brandbaar materiaal. Een beetje is geen probleem.<br />
# Schakel uit:<br />
#* Laser: sleutel draaien, indicatorlampje wordt rood.<br />
<br />
== Tijdens ==<br />
<br />
Let op: deze instructies zijn bedoeld als geheugensteuntje en zijn niet genoeg om elke situatie aan te kunnen. Je '''MOET''' persoonlijk lasercutter-instructies hebben gehad om de machine te mogen gebruiken.<br />
<br />
* Blijf in de buurt van de lasercutter en kijk vaak hoe het gaat. Als de air assist kapot gaat tijdens gebruik, is de kans op vlammen groot.<br />
<br />
=== In geval van brand in de machine ===<br />
<br />
[[Image:Dontpanic.jpg|thumb|Blijf kalm: eerst nadenken, dan doen. Paniek helpt niet.]]<br />
# Bedien de noodstop, om de hoogspanningsvoeding uit te schakelen.<br />
# Observeer het brandende materiaal. Meestal zal de vlam zelf doven als de laser geen energie meer toevoegt.<br />
# Als je het brandende materiaal veilig kunt beetpakken, kun je het uit de machine halen en op de vloer gooien. (Een stukje vloer vervangen is goedkoper dan de lasercutter vervangen, ook al is het veel en erg vervelend werk.)<br />
# Als je in de machine moet blussen, gebruik dan eerst de CO2-blusser. Hou de spuitmond dicht bij de slang vast, omdat anders je hand vast kan vriezen. Zet de CO2-blusser voor de lasercutter, trek de borgpin eruit, en spuit de machine vol koolzuurgas.<br />
# Als het met de CO2-blusser niet heeft gewerkt (dat kan, want CO2-blussers zijn niet fantastisch voor het blussen van vaste stoffen), gebruik dan alsnog een schuimblusser als laatste redmiddel.<br />
<br />
De klep van de lasercutter is van polycarbonaat. Polycarbonaat is vlamvertragend, dus laat de klep dicht totdat je een plan hebt bedacht.<br />
<br />
=== LaserWeb ===<br />
<br />
# Sluit LightBurn en start LaserWeb. Ze moeten niet tegelijk draaien.<br />
# In het tabblad "Comms":<br />
## klik "connect".<br />
# In het tabblad "Control":<br />
## Jog Z <span style="background: #ff8; border: 1px solid #888">&uarr; &darr;</span> laag genoeg om je materiaal te kunnen plaatsen; leg daarna je materiaal op het bed.<br />
## Jog X <span style="background: #f88; border: 1px solid #888">&larr; &rarr;</span> en Y <span style="background: #8f8; border: 1px solid #888">&uarr; &darr;</span> naar boven je materiaal (bij krom materiaal zoals hout: op het hoogste punt!)<br />
## Home Z<br />
## Home all (deze doet Z niet)<br />
## Set zero<br />
## Beide refresh-knopjes bij F en S, die beide op 100% moeten staan<br />
# In het tabblad "Files":<br />
## Open je bestand(en)<br />
## Sleep de tekening naar de juiste plek op het bed<br />
## Voeg bewerkingen toe (drag/drop of "add single")<br />
## Stel per bewerking de juiste parameters in<br />
##* '''Laser cut''': snijden of lijngraveren (graveren is gewoon snijden met te weinig vermogen om door het materiaal heen te komen)<br />
##* '''Laser fill path''': lasergraveren met een efficient vulpatroon: behoorlijk snel, maar verhit het materiaal lokaal heel erg<br />
##* '''Laser raster''': lasergraveren, werkt alleen op bitmapplaatjes, voor power geef je ondergrens en bovengrens aan in plaats van 1 waarde<br />
##* '''Laser raster merge''': lasergraveren, maar werkt op vectoren. Hou er rekening mee dat het vermogen gevarieerd wordt aan de hand van de kleur van het object.<br />
##* "Start height (mm)" en "pass depth (mm)" altijd alleen op '''negatieve getallen''' instellen, dus voor 20 mm afstand vul je '''&ndash;20''' in. Voor scherpe lijnen moet je deze beide waarden op 0 laten staan.<br />
##* In de meest gevallen zul je alleen "Laser power (%)" en "Cut rate (mm/s)" instellen.<br />
## "Generate G-code"<br />
# In het tabblad "Control":<br />
## Run<br />
# Blijf bij het apparaat!<br />
#* Kijk regelmatig naar de voortgang<br />
#* Grijp snel in als het fout gaat<br />
<br />
=== LightBurn ===<br />
<br />
# Sluit LaserWeb en start LightBurn. Ze moeten niet tegelijk draaien.<br />
# In het tabblad "Console":<br />
## Klik "fix shit". (Dit is alleen nodig als LaserWeb tussendoor is gebruikt, maar het kan geen kwaad om het altijd te doen na het homen.)<br />
# Importeer bestanden in je werkblad of teken rechtstreeks vectoren in het werkblad.<br />
# In het tabblad "Move":<br />
## Zet de snelheid op 100.<br />
## Beweeg Z ↑ ↓ laag genoeg om je materiaal te kunnen plaatsen; leg daarna je materiaal op het bed.<br />
##* Let op: Z↑ beweegt virtueel de "kop" omhoog, maar in feite beweegt het bed omlaag. De pijlen zijn dus omgekeerd ten opzichte van de richting die het bed verplaatst.<br />
## Kies de positiemarker en plaats de kop boven je materiaal door in het werkblad te klikken. Dat is makkelijker dan X en Y bewegen met de pijltjes.<br />
## Klik "Focus Z" (bij krom materiaal zoals hout: op het hoogste punt!).<br />
# In het tabblad "Cut":<br />
## Kies voor de lagen (lagen worden weergegeven als verschillende kleuren) de juiste parameters<br />
##* '''Cut''': snijden of lijngraveren (graveren is gewoon snijden met te weinig vermogen om door het materiaal heen te komen)<br />
##* '''Scan''': lasergraveren. voor power geef je ondergrens en bovengrens aan in plaats van 1 waarde<br />
##** '''Overscan''' geeft brandplekken buiten het plaatje, maar als je het uitzet krijg je donkere zijkanten in het plaatje. Wordt misschien in een toekomstige versie opgelost.<br />
##** '''Flood fill''' geeft een effect zoals "Laser fill path" in LaserWeb<br />
##* '''Scan and cut''': doet eerst het een, dan het ander.<br />
#* Z-afstanden vul je in als LightBurn '''negatieve getallen'''. Voor scherpe lijnen gebruik je 0.<br />
#* [[File:DIN4844-brandgevaarlijke-stoffen.svg|30px]] Air assist kan worden uitgeschakeld (voor sommige uitzonderlijke bewerkingen is dat nodig) en de laatstgekozen instelling per laagkleur blijft bewaard. Dat maakt het geheel veel brandgevaarlijker. Controleer altijd voor '''elke kleur/laag''' of air assist ingeschakeld is!<br />
# In het tabblad "Laser":<br />
## Klik op Frame om met de laser uitgeschakeld te zien waar je job zal komen.<br />
## Klik op het play-icoon. Er is geen aparte tussenstap voor het genereren van gcode.<br />
# Blijf bij het apparaat!<br />
#* Kijk regelmatig naar de voortgang<br />
#* Grijp snel in als het fout gaat<br />
<br />
== Na ==<br />
<br />
# Neem alle restanten van het bed<br />
# Sluit de software van de laptop netjes af (start, afsluiten)<br />
# Zet de stroomslof uit zodra de laptop is afgesloten<br />
# Reken af via revbank met <code>give lasercutter 10</code> (of een ander bedrag: € 2,50 per kwartier dat je de machine bezet hield, afgerond naar boven).<br />
# Zijn er voorraden bijna op? Waren er andere problemen? Mail board of los dit zelf op.<br />
# Vul tot slot de wiki aan met jouw ervaringen qua materialen en settings<br />
<br />
=Materialen en ervaringen=<br />
<br />
{|class=wikitable,<br />
! Materiaal !! Snijden !! Graveren !! Opmerkingen<br />
|-<br />
| Stof || ja || dik stof (denim) || kleine stukjes vliegen weg<br />
|-<br />
| Vilt || ja || nee || brandgevaarlijk, kleine stukjes vliegen weg<br />
|-<br />
| Papier || ja || karton || brandgevaarlijk, kleine stukjes vliegen weg<br />
|-<br />
| MDF || max. 6 mm || ja || stinkt, vormvast<br />
|-<br />
| Hardboard || ja || ja || Geen last van rafelende randjes zoals bij zagen, verkoolt nauwelijks. Wel goed recht buigen voor 't laseren. Getest met 3 mm.<br />
|-<br />
| Hardboard, watervast || ja || ja || Sterker en minder krom dan normaal hardboard, maar wel 3x zo duur. Getest met 3 mm.<br />
|-<br />
| Triplex (populier) || max. 10 mm || ja ||<br />
|-<br />
| Triplex (berken) || max. 8 mm || ja<br />
|-<br />
| Triplex (powerplex) || max. 8 mm || ja<br />
|-<br />
| Balsa || ja || nee || kleine stukjes vliegen weg<br />
|-<br />
| Leer || ja || ja || stinkt echt vreselijk, hier maak je geen vrienden mee<br />
|-<br />
| Acrylglas aka acrylaat aka plexiglas aka PMMA || max. 8 mm || ja || gegoten (cast) materiaal is veel mooier dan geextrudeerd<br />
|-<br />
| Glas || nee || ja || zandstraal-effect<br />
|-<br />
| Spiegel || nee || ja || voorkant: net als glas<br>achterkant: spiegellaag weg + zandstraaleffect. Goede ervaring op ikea-spiegeltje met raster .1 diameter, 0-30% vol zwart, 45 mm/s, -2 mm start height, 5 mm overscan, burn white uit.<br />
|-<br />
| Keramiek || nee || ja<br />
|-<br />
| Graniet || nee || ja<br />
|-<br />
| Gecoat/geanodiseerd metaal || nee || ja || je snijdt de coating weg, het metaal wordt zichtbaar<br />
|-<br />
| Ongecoat metaal || nee || zelden || Sommige metalen zijn rechtstreeks te graveren. Staal is vaak te etsen als je het (buiten!) met [https://www.conrad.nl/p/crc-32660-aa-glijlak-500-ml-1533116 Dry Moly] voorbehandelt: extreem ontvetten, dan 3 dunne coatings aanbrengen met 2 minuten droogtijd na elke laag, daarna lasergraveren. Residu wegpoetsen met alcohol.<br />
|-<br />
|-<br />
| [[Macbook_Graveren|Macbooks]] || nee || ja || werkt geweldig, pas op voor het plexiglazen appeltje!<br />
|-<br />
| [[Thinkpad_Graveren|Thinkpads]] || nee || ja || Alleen met de typen waarbij er een magnesium plaat onder de coating zit!<br />
|-<br />
| Magneetplaat van Drukwerkdeal || ja || - || Zelfs 1 cm snijden stinkt al gigantisch; niet doen dus.<br />
|}<br />
=Dauwpunt=<br />
<br />
Bij hoge relatieve luchtvochtigheid kan de chiller (die tot +- 16 koelt) voor condensatie zorgwn. Dit willen we niet, i.v.m. gevaar voor jezelf (hoogspanning) en beschadiging van de machine (of je project). Dus: houd de luchtvochtigheid en temperatuur op de space in de gaten en gebruik de lasercutter niet als het dauwpunt hoger dan 15 graden ligt. <br />
<br />
<br />
Tabel: Dauwpunttabel voor verschillende waarden van de relatieve luchtvochtigheid<br />
Relatieve Luchtvochtigheid<br />
Lucht<br />
Temperatuur<br />
100 90 80 70 60 50 40 30 20<br />
-20 -20,0 -21,2 -22,5 -24,0 -25,7 -27,7 -30,1 -33,1 -37,1<br />
-18 -18,0 -19,2 -20,6 -22,1 -23,8 -25,9 -28,3 -31,3 -35,4<br />
-16 -16,0 -17,3 -18,6 -20,2 -22,0 -24,0 -26,5 -29,5 -33,7<br />
-14 -14,0 -15,3 -16,7 -18,3 -20,1 -22,1 -24,6 -27,8 -32,0<br />
-12 -12,0 -13,3 -14,7 -16,3 -18,2 -20,3 -22,8 -26,0 -30,3<br />
-10 -10,0 -11,3 -12,8 -14,4 -16,3 -18,4 -21,0 -24,3 -28,7<br />
-8 -8,0 -9,3 -10,8 -12,5 -14,4 -16,6 -19,2 -22,5 -27,0<br />
-6 -6,0 -7,4 -8,9 -10,6 -12,5 -14,7 -17,4 -20,7 -25,3<br />
-4 -4,0 -5,4 -6,9 -8,7 -10,6 -12,9 -15,6 -19,0 -23,6<br />
-2 -2,0 -3,4 -5,0 -6,7 -8,7 -11,0 -13,8 -17,2 -21,9<br />
0 0,0 -1,4 -3,0 -4,8 -6,8 -9,2 -12,0 -15,5 -20,3<br />
2 2,0 0,5 -1,1 -2,9 -4,9 -7,3 -10,2 -13,7 -18,6<br />
4 4,0 2,5 0,9 -1,0 -3,1 -5,5 -8,4 -12,0 -16,9<br />
6 6,0 4,5 2,8 0,9 -1,2 -3,6 -6,6 -10,3 -15,3<br />
8 8,0 6,5 4,8 2,9 0,7 -1,8 -4,8 -8,5 -13,6<br />
10 10,0 8,4 6,7 4,8 2,6 0,1 -3,0 -6,8 -11,9<br />
12 12,0 10,4 8,7 6,7 4,5 1,9 -1,2 -5,0 -10,3<br />
14 14,0 12,4 10,6 8,6 6,4 3,7 0,6 -3,3 -8,6<br />
16 16,0 14,4 12,5 10,5 8,2 5,6 2,4 -1,6 -7,0<br />
18 18,0 16,3 14,5 12,4 10,1 7,4 4,2 0,2 -5,3<br />
20 20,0 18,3 16,4 14,4 12,0 9,3 6,0 1,9 -3,6<br />
22 22,0 20,3 18,4 16,3 13,9 11,1 7,8 3,6 -2,0<br />
24 24,0 22,3 20,3 18,2 15,7 12,9 9,6 5,3 -0,4<br />
26 26,0 24,2 22,3 20,1 17,6 14,8 11,3 7,1 1,3<br />
28 28,0 26,2 24,2 22,0 19,5 16,6 13,1 8,8 2,9<br />
30 30,0 28,2 26,2 23,9 21,4 18,4 14,9 10,5 4,6<br />
32 32,0 30,1 28,1 25,8 23,2 20,3 16,7 12,2 6,2<br />
34 34,0 32,1 30,0 27,7 25,1 22,1 18,5 13,9 7,8<br />
36 36,0 34,1 32,0 29,6 27,0 23,9 20,2 15,7 9,5<br />
38 38,0 36,1 33,9 31,6 28,9 25,7 22,0 17,4 11,1<br />
40 40,0 38,0 35,9 33,5 30,7 27,6 23,8 19,1 12,7<br />
42 42,0 40,0 37,8 35,4 32,6 29,4 25,6 20,8 14,4<br />
44 44,0 42,0 39,8 37,3 34,5 31,2 27,3 22,5 16,0<br />
46 46,0 43,9 41,7 39,2 36,3 33,0 29,1 24,2 17,6<br />
48 48,0 45,9 43,6 41,1 38,2 34,9 30,9 25,9 19,2<br />
50 50,0 47,9 45,6 43,0 40,1 36,7 32,6 27,6 20,8<br />
<br />
(Ja, dit moet nog een echte tabel worden en de gevarenzones in geel/rood. Het is een wiki...)<br />
bron:<br />
De website: www.tabellenboekje.nl (Dauwpunttabel voor verschillende waarden van de relatieve luchtvochtigheid), zie http://www.tabellenboekje.nl/meteo-tabel-dauwpunt.php#dauwpunt-x-relatieve-vochtigheid</div>Luteijnhttps://revspace.nl/index.php?title=Bordspellenavond&diff=21304Bordspellenavond2019-03-02T16:03:42Z<p>Luteijn: </p>
<hr />
<div>{{Event<br />
|Name=Bordspellenavond<br />
|DateStart=1 Mar 2019<br />
|DateEnd=1 Mar 2019<br />
|InfoLocation=Overgoo 1, Leidschendam<br />
|InfoOpen=19:30<br />
}}<br />
<br />
Vrijdag 1 Maart komt er weer een bordspellenavond!<br />
Neem je eigen bordspellen mee en bij starttijd wordt er bepaald wat er wordt gespeeld<br />
en met hoeveel mensen.<br />
Avond wordt gehouden in de keuken.<br />
<br />
= Spellen = <br />
Schrijf hier of je komt en of je spellen meeneemt.<br />
<br />
{| class="wikitable"<br />
|-<br />
!naam<br />
!spel<br />
|-<br />
|SelfishPopcorn || Axis and allies 1914 en This war of mine Boardgame<br />
|-<br />
|luteijn (hopelijk +3, misschien -1) || Waarschijnlijk wel wat; b.v. FRAG, Flashpoint of OGRE<br />
|-<br />
|Peetz0r || 4x Monopoly: regulier (ƒ), Suriname (SRD), Beurs (€M), Scouts (£)<br />
|-<br />
|}</div>Luteijnhttps://revspace.nl/index.php?title=Bordspellenavond&diff=21283Bordspellenavond2019-02-25T16:43:56Z<p>Luteijn: </p>
<hr />
<div>{{Event<br />
|Name=Bordspellenavond<br />
|DateStart=1 Mar 2019<br />
|DateEnd=1 Mar 2019<br />
|InfoLocation=Overgoo 1, Leidschendam<br />
|InfoOpen=19:30<br />
}}<br />
<br />
Vrijdag 1 Maart komt er weer een bordspellenavond!<br />
Neem je eigen bordspellen mee en bij starttijd wordt er bepaald wat er wordt gespeeld<br />
en met hoeveel mensen.<br />
Avond wordt gehouden in de keuken.<br />
<br />
= Spellen = <br />
Schrijf hier of je komt en of je spellen meeneemt.<br />
<br />
{| class="wikitable"<br />
|-<br />
!naam<br />
!spel<br />
|-<br />
|SelfishPopcorn || Axis and allies 1914 en This war of mine Boardgame<br />
|-<br />
|luteijn (hopelijk +3, misschien -1) || Waarschijnlijk wel wat; b.v. FRAG, Flashpoint of OGRE<br />
|-<br />
|}<br />
<br />
<br />
PSA, wellicht interessant voor spellenfanaten die onder een spreekwoordelijke steen leven en het gemist hebben - Steve Jackson Games gaat de befaamde 'pocket boxen' heruitgeven via kickstarter. Zie https://www.kickstarter.com/projects/sjgames/pocket-box-games-of-the-eighties/description - je moet wel ruim in het geld zitten of een soort groupbuy doen want er zijn zoveel interessante spellen etc. bij dat je makkelijk richting de $500 kan gaan en nog niet /alles/ hebt.</div>Luteijnhttps://revspace.nl/index.php?title=Supersamla&diff=21282Supersamla2019-02-25T16:03:31Z<p>Luteijn: /* Huidige plattegrond */</p>
<hr />
<div> {{Project<br />
|Name=Supersamla<br />
|Picture=supersamla.png<br />
|Omschrijving=Samen een paar vierkante meters huren!<br />
|Status=Initializing<br />
|Contact=<br />
}}<br />
<br />
<br />
=BELANGRIJK=<br />
Supersamla GIET OAN. Inschrijven is je vastleggen aan de opgegeven hoeveelheid en prijs voor een minimum van 6 maanden.<br />
<br />
=Inleiding=<br />
De ruimte tussen de garagedeur en de space komt vrij. CA 65 m² en de space gaat dat huren per 1 maart, mogelijk in het begin met minder ruimte omdat er nog meubels staan in een deel. We zoeken grofweg een startgroep van 15 kavels.<br />
<br />
=Wat mag wel/niet=<br />
==Wel==<br />
*Opslag<br />
*Voor de rode kavels: Projecten/werken (binnen kaders: ivm geen luchtverversing kunnen er max 2 mensen langer achter elkaar veilig werken, en er is geen elektra aanwezig)<br />
<br />
==Niet==<br />
*Auto sleutelen of andere dingen met veel stof of stank/giftige gassen/middelen<br />
*Slapen/wonen<br />
*Apparatuur ingeschakeld achterlaten<br />
*Brandstof stallen (ook niet in 'n voertuig)<br />
<br />
=Mogelijkheden=<br />
[[File:Supersamla.png|600px]]<br />
<br />
We hebben twee opties voor je:<br />
* Een tweetal stellingkasten voor 25 euro per maand. Ongeveer 90cm breed, 45cm diep en 1,75m hoog. Deze staan tegenover elkaar met een pad ertussen, je mag dit overkappen/volbouwen tot het plafond. Je mag 15cm uit de kast uitsteken. Op de kaart zijn deze kastjes blauw ingetekend. <br />
* Een kaal stukje 'grond' van 1 bij 1,3m voor 15 euro per maand. Hier mag je neerzetten wat je wilt zolang het veilig is voor iedereen. Deze vlakjes zijn rood ingetekend. Je kunt uiteraard meerdere stukjes grond combineren tot een grotere ruimte.<br />
<br />
=Verdienmodel=<br />
We gaan uit van een minimale bezetting van 50% om dit rendabel te laten zijn voor de space. Er is een investering nodig om dit te kunnen doen die terugverdiend moet worden en daarna is er wat winst die ten goede komt van de space.<br />
<br />
=Minimale afnametijd/Beeindiging=<br />
* Je neemt tijdens de kickstarter voor minimaal 6 maanden af.<br />
* Opzegtermijn vanuit space is 1 maand<br />
<br />
<br />
=Aanmeldingen=<br />
==Kastparen==<br />
(2 kasten voor 25 euro)<br />
<br />
===Huidige plattegrond===<br />
<br />
{|class=wikitable<br />
|-<br />
| 4||3||2||1||0 (spacemuur)<br />
|-<br />
| ? || ? || ? || luteijn || cmpxchg<br />
|-<br />
| ? || ? || ? || luteijn || cmpxchg<br />
|}<br />
<br />
{|class=wikitable<br />
|-<br />
! Naam !! Aantal kastparen <br />
|-<br />
| Folkert || 1<br />
|-<br />
| Luteijn || 1<br />
|-<br />
| straw || 1<br />
|-<br />
| cmpxchg || 1<br />
|-<br />
| ... || ?<br />
|-<br />
|}<br />
<br />
==Lapjes projectgrond==<br />
(1 vlakje voor 15 euro), pad is 90cm breed<br />
<br />
===Huidige plattegrond===<br />
<br />
{|class=wikitable<br />
|-<br />
| 7 || 6||5||4||3||2||1||0 (spacemuur)<br />
|-<br />
| gori || Lucanator || Sebastius || Benadski || PBX || PBX || PBX || h3x4d3c1m4l<br />
|-<br />
|}<br />
<br />
===Aanmeldingen===<br />
{|class=wikitable<br />
|-<br />
! Naam !! Aantal vlakjes van 1,3m²<br />
|-<br />
| h3x4d3c1m4l || 1<br />
|-<br />
| pbx || 3<br />
|-<br />
| sebastius || 1<br />
|-<br />
| benadski || 1<br />
|-<br />
| lucanator || 1<br />
|-<br />
| Gori || 1<br />
|-<br />
| ... || ?<br />
|-<br />
|}<br />
Momenteel zijn we qua vlakjes vol, praat even met een bestuurslid voor de mogelijkheden.</div>Luteijnhttps://revspace.nl/index.php?title=Supersamla&diff=20983Supersamla2019-01-23T05:49:57Z<p>Luteijn: /* Aanmeldingen */</p>
<hr />
<div> {{Project<br />
|Name=Kamertjeverhuur<br />
|Picture=<br />
|Omschrijving=Samen een paar vierkante meters huren!<br />
|Status=Initializing<br />
|Contact=<br />
}}<br />
<br />
=BELANGRIJK=<br />
Dit is een kickstarter. Inschrijven is je vastleggen aan de opgegeven hoeveelheid en prijs voor een minimum van 6 maanden.<br />
<br />
=Inleiding=<br />
De ruimte tussen de garagedeur en de space komt vrij. CA 65 m² en de space kan dat mogelijk huren als er voldoende animo is. We zoeken grofweg een startgroep van 10 deelnemers.<br />
<br />
=Wat mag wel/niet=<br />
==Wel==<br />
*Opslag<br />
*Voor de rode kavels: Projecten/werken (binnen kaders: ivm geen luchtverversing kunnen er max 2 mensen veilig werken, en er is geen elektra aanwezig)<br />
<br />
==Niet==<br />
*Auto sleutelen of andere dingen met veel stof of stank/giftige gassen/middelen<br />
*Slapen/wonen<br />
*Apparatuur ingeschakeld achterlaten<br />
*Brandstof stallen (ook niet in 'n voertuig)<br />
<br />
=Mogelijkheden=<br />
[[File:Supersamla.png|600px]]<br />
<br />
We hebben twee opties voor je:<br />
* Een tweetal stellingkasten voor 25 euro per maand. Ongeveer 90cm breed, 45cm diep en 2m hoog. Deze staan tegenover elkaar met een pad ertussen, je mag dit overkappen/volbouwen tot het plafond. Je mag 15cm uit de kast uitsteken. Op de kaart zijn deze kastjes blauw ingetekend. <br />
* Een kaal stukje 'grond' van 1 bij 1,3m voor 15 euro per maand. Hier mag je neerzetten wat je wilt zolang het veilig is voor iedereen. Deze vlakjes zijn rood ingetekend.<br />
<br />
=Verdienmodel=<br />
We gaan uit van een minimale bezetting van 50% om dit rendabel te laten zijn voor de space. Er is een investering nodig om dit te kunnen doen die terugverdiend moet worden en daarna is er wat winst die ten goede komt van de space.<br />
<br />
=Minimale afnametijd/Beeindiging=<br />
* Je neemt tijdens de kickstarter voor minimaal 6 maanden af.<br />
* Opzegtermijn vanuit space is 1 maand<br />
<br />
<br />
=Aanmeldingen=<br />
Kastparen (2 kasten voor 25 euro, 4 kasten voor 45 euro, 6 kasten voor 60 euro), pad is 90cm breed.<br />
<br />
{|class=wikitable<br />
|-<br />
! Naam !! Aantal kastparen <br />
|-<br />
| Folkert || 1<br />
|-<br />
| Luteijn || 1<br />
|-<br />
| ... || ?<br />
|-<br />
|}<br />
<br />
Lapjes projectgrond (1 vlakje voor 15 euro), pad is 90cm breed<br />
{|class=wikitable<br />
|-<br />
! Naam !! Aantal vlakjes van 1,3m²<br />
|-<br />
| h3x4d3c1m4l || 1<br />
|-<br />
| ... || ?<br />
|-<br />
|}</div>Luteijnhttps://revspace.nl/index.php?title=Bordspellenavond&diff=20559Bordspellenavond2018-12-12T10:01:32Z<p>Luteijn: </p>
<hr />
<div>{{Event<br />
|Name=Bordspellenavond<br />
|DateStart=17 Dec 2018<br />
|DateEnd=17 Dec 2018<br />
|InfoLocation=Overgoo 1, Leidschendam<br />
|InfoOpen=18:00<br />
}}<br />
<br />
Maandag 17 December komt de allereerste bordspellenavond!<br />
Neem je eigen bordspellen mee en bij starttijd wordt er bepaald wat er wordt gespeeld<br />
en met hoeveel mensen.<br />
Avond wordt gehouden in de keuken.<br />
<br />
= Spellen = <br />
Schrijf hier of je komt en of je spellen meeneemt.<br />
<br />
{| class="wikitable"<br />
|-<br />
!naam<br />
!spel<br />
|-<br />
|SelfishPopcorn || moet nog kijken <br />
|-<br />
|benadski || anti-monopoly ligt al jaren in de kast, nooit gespeeld... :P <br />
|-<br />
|Peetz0r || 3x Monopoly (regulier, Suriname, Beurs)<br />
|-<br />
|hvwees || De Kolonisten van de Lage Landen (3 - 5 spelers) (Eventueel kan ik Exploding kittens meenemen (2-5 spelers) is alleen een kaartspel en geen bordspel ;-) )<br />
|-<br />
|Eightdot || moet nog kijken<br />
|-<br />
|lorenz +1 || Bunny Kingdom<br />
|-<br />
|Danlie || Evolution, Vikings of the North Sea etc. If you have any wishes let me know I can borrow a lot of game!<br />
|-<br />
|lucanator || Jenga<br />
|-<br />
|luteijn || I have quite a few tabletop games (board/card/etc.), will see what to bring, perhaps a few shorter 1-1 games instead of huge 5+ player games...<br />
|-<br />
|}</div>Luteijnhttps://revspace.nl/index.php?title=Bordspellenavond&diff=20558Bordspellenavond2018-12-12T09:57:29Z<p>Luteijn: /* Spellen */</p>
<hr />
<div>{{Event<br />
|Name=Bordspellenavond<br />
|DateStart=17 Dec 2018<br />
|DateEnd=17 Dec 2018<br />
|InfoLocation=Overgoo 1, Leidschendam<br />
|InfoOpen=18:00<br />
}}<br />
<br />
Maandag 17 December komt de allereerste bordspellenavond!<br />
Neem je eigen bordspellen mee en bij starttijd wordt er bepaald wat er wordt gespeeld<br />
en met hoeveel mensen.<br />
Avond wordt gehouden in de keuken.<br />
<br />
= Spellen = <br />
Schrijf hier of je komt en of je spellen meeneemt.<br />
<br />
{| class="wikitable"<br />
|-<br />
!naam<br />
!spel<br />
|-<br />
|SelfishPopcorn || moet nog kijken <br />
|-<br />
|benadski || anti-monopoly ligt al jaren in de kast, nooit gespeeld... :P <br />
|-<br />
|Peetz0r || 3x Monopoly (regulier, Suriname, Beurs)<br />
|-<br />
|hvwees || De Kolonisten van de Lage Landen (3 - 5 spelers) (Eventueel kan ik Exploding kittens meenemen (2-5 spelers) is alleen een kaartspel en geen bordspel ;-) )<br />
|-<br />
|Eightdot || moet nog kijken<br />
|-<br />
|lorenz +1 || Bunny Kingdom<br />
|-<br />
|Danlie || Evolution, Vikings of the North Sea etc. If you have any wishes let me know I can borrow a lot of game!<br />
|-<br />
|lucanator || Jenga<br />
|-<br />
|luteijn || I have quite a few tabletop games (board/card/etc.), will see what to bring.<br />
|-<br />
|}</div>Luteijnhttps://revspace.nl/index.php?title=MainsFrequency&diff=18262MainsFrequency2018-04-23T16:28:08Z<p>Luteijn: /* Other projects */ link to a similar schematic using 220 nF capacitor and 2MOhm bleeder resistor</p>
<hr />
<div> {{Project<br />
|Name=MainsFrequency<br />
|Picture=dropper_opto.png<br />
|Omschrijving=A simple mains frequency counter<br />
|Status=Initializing<br />
|Contact=bertrik<br />
}}<br />
<br />
<br />
== Introduction ==<br />
This page is about creating a simple frequency counter for mains power and publish the frequency over MQTT.<br />
<br />
It's based on the Arduino platform, using an ESP8266 to do the wifi/network/MQTT stuff.<br />
<br />
The frequency measurement principle is to count the number of mains cycles in a fixed period.<br />
To get a resolution of 0.01 Hz, the period is 100 seconds.<br />
<br />
To keep the measurement circuit relatively safe, only a small part of the electronics is actually connected to mains.<br />
<br />
== Hardware ==<br />
The plan is to use an ESP8266 because it can easily publish the measured value over wifi/MQTT.<br />
<br />
I'm looking at various ways of actually getting the mains signal into the microcontroller.<br />
The simplest way appears to be an optocoupler in series with R, C and a diode anti-parallel over the optocoupler's LED.<br />
<br />
* could be a transformer, like [http://jorisvr.nl/article/grid-frequency shown here]. Safe but bulky.<br />
* could be a capacitive/resistive dropper with an optocoupler, like [https://forum.arduino.cc/index.php?PHPSESSID=mbv69trkc089m30l4shlo4cl97&topic=192063.msg1419646#msg1419646 the circuit in this post] (note: this circuit is for 120V!)<br />
* <s>could be [https://nl.aliexpress.com/item/ding/32828199766.html this thing] from aliexpress</s><br />
<br />
=== Aliexpress circuit ===<br />
[[File:ali_230v_schema.png|thumb|right]]<br />
<br />
The circuit as reverse engineered is shown on the right. Basically, the mains is rectified through a high-value high-power resistor and a bridge rectifier, then stabilized with a zener to 5.1V, which lights the green LED and activates the optocoupler to pull the output low when mains is present. The 47k pull-up makes the signal high when there is no mains power available.<br />
<br />
So this circuit doesn't actually do what I want: the opto is activated all the time when 230V is present, it's not flashing at 50 Hz.<br />
<br />
=== Circuit ===<br />
[[File:dropper_opto.png|thumb|right]]<br />
The circuit on the right is the one I plan to use.<br />
<br />
The capacitor drops most of the voltage. It should be an x-rated type, you can tell by the marking "X2".<br />
The resistor (1k or so) limits the inrush current.<br />
The diode (1N4148 probably) protects the optocoupler (4N27 or 4N35) from reverse voltage.<br />
The resistor on the right is a pull-up resistor, possibly not even needed if I use the built-in pull-up of the microcontroller.<br />
<br />
The X-capacitor, even though it is designed for connection to mains voltage and referred to as a "safety capacitor", has a failure mode to most likely fail as a short.<br />
This means additional components are needed to make it actually safe to use in this circuit, like a fuse in series that blows when the capacitor fails.<br />
(maybe I should just use an Y-capacitor then, which has a failure mode of most likely failing open)<br />
<br />
At 50 Hz, the capacitor has a reactance of about 32 kOhm, so the LED current should be something like 230V/32kOhm = 7 mA, well below the maximum of 60 mA mentioned in the data sheet.<br />
Possibly, a bleeder resistor could be put parallel to the capacitor, to make sure that the capacitor doesn't stay charged when the circuit is powered off.<br />
Power consumed in the resistor is R*I*I so about 50 mW, so we can probably use the very common 250 mW type there.<br />
<br />
== Software ==<br />
See [https://github.com/bertrik/mainsfreq the github page]. <br />
<br />
The working principle is that we count the number of cycles in a 100 second period, this should nominally be 5000.<br />
A cycle count is done every second and the result is put in a circular buffer of 100 bins.<br />
The average of these 100 bins then provides the frequency over the past 100 seconds.<br />
<br />
The accuracy of the frequency count depends on the accuracy of the crystal (among other things).<br />
To get 0.01 Hz error at 50 Hz, we need an time reference with at most 0.01 / 50 = 200 ppm frequency deviation.<br />
This is probably doable with the built-in crystal on a typical ESP8266 board (like a Wemos D1 mini).<br />
<br />
== Other projects ==<br />
* http://www.palebluedot.nl/jml/projects/arduino/43-measure-mains-power-frequency<br />
* https://engmousaalkaabi.blogspot.nl/2016/11/ac-220v-frequency-counter-using-arduino.html<br />
<br />
* https://a01.veron.nl/download/hamnieuws/2016/Ham-06-2016.pdf (see page 20, 220nf, 2M Ohm bleeder)</div>Luteijnhttps://revspace.nl/index.php?title=IRC&diff=18227IRC2018-04-19T19:27:04Z<p>Luteijn: /* Freenode #revspace */</p>
<hr />
<div><br />
= Freenode #revspace =<br />
<br />
Of course, endless discussion on IRC is part of the game. Therefore, a channel has been claimed on [https://freenode.net/ freenode]:<br />
<br />
irc://chat.freenode.net/revspace<br />
<br />
<br />
Just connect to [https://freenode.net/irc_servers.shtml one of the many] freenode servers, and /join #revspace to join the chatter!<br />
<br />
If you do not have an IRC client installed, you can try [https://webchat.freenode.net/?channels=revspace freenode's webchat application].<br />
<br />
<br />
Secret decoder rings:<br />
<br />
https://weechat.org/files/scripts/pending/foo.pl<br />
<br />
https://github.com/irssi/scripts.irssi.org/blob/master/scripts/foo.pl</div>Luteijnhttps://revspace.nl/index.php?title=Muggenrace&diff=18197Muggenrace2018-04-16T12:59:12Z<p>Luteijn: linkje naar uitleg mode-1/2</p>
<hr />
<div> {{Project<br />
|Name = Muggenrace<br />
|Picture = Mug.jpg<br />
|Omschrijving = Quadcopter race<br />
|Status = In progress<br />
|Contact = f0x<br />
|Contact1 = Crashjuh<br />
}}<br />
<br />
{{Event<br />
|Name=Muggenrace<br />
|DateStart=1 Jun 2018 16:00<br />
|InfoLocation=Overgoo 1, Leidschendam<br />
|InfoOpen=16:00<br />
}}<br />
<br />
Idee: koop allemaal (op tijd!) een Eachine H8 Mini quadcopter, [[https://www.aliexpress.com/item/Eachine-H8-Mini-Headless-RC-Helicopter-Mode-2-4G-4CH-6-Axle-Quadcopter-RTF-Remote-Control/32744422553.html Aliexpress]], kost maar 1 tientje. Oefen zo veel je wil, en dan op 1 Juni racen.<br />
<br />
{|class=wikitable<br />
! Aanmeldingen || opmerkingen<br />
|-<br />
| f0x || 2 muggen, paar batterijtjes en oplader <br />
|-<br />
| bertrik || (nieuwe besteld), ik neem ook een aantal reservebatterijtjes mee<br />
|-<br />
| Sebastius || 2 Muggen onderweg, samen met een flinke stapel batterijen en twee enge opladertjes.<br />
|-<br />
| Maxell || Mode 2 modelletje is onderweg, kans is groot dat ik er geen pepernoot van bak :D<br />
|-<br />
| Jouw naam hier? ||<br />
|}<br />
<br />
http://www.modelbouw.nl/wiki/mode-1-of-mode-2-kiezen-voor-je-zender</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17797Luteijn/Thermostat2018-03-13T10:44:36Z<p>Luteijn: </p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does. Another option is a 24V tolerant optocoupler. I'll try that as it should take a lot less power than a relay, but still provide some isolation from the 24V. Ordered a batch of PC817's from China, thes should handle up to 50mA and 35V, which should be plenty (see under power from heater section)<br />
<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there. Might just skip this step and make a wireless bridge right away.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT. Voltage on the thermostat side seems to be available, so the ESP can use the Chronotherm for power. Would be nice to program it to be energy conservative.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* Remote controlled radiator(knobs) don't seem needed at first, just need to make sure the thermostatic knobs are set to match the desired max temperature, and trust the system to run the heater as long as at least one room needs heat. Eventually it would make things even better to have remote controlled radiators, so a room can be kept cooler than its max even if there is another room that needs to heat up. Should be a relatively simple operation to pop off the existing knobs and replace them by remote controlled ones that can be set to the desired temperature by the system, or just opened/closed more based on the heat demand from the room they are in.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== faceplate ====<br />
draft version [[file:Faceplate.svg]]<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|keepalive<br />
|state="alive"|"recovering"|"leaving"|"power"|<whatever><br />
|Periodic message to report (continued) presence, intention to leave, power issues (battery low etc.), and/or recovering from e.g. power outage, reset etc.<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|<value><br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Control node =====<br />
This will be located near a/the heater boiler/furnace (or airco, or ventilator or whatever) and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
* Desires and Engages directed to it<br />
<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit =====<br />
Basically just produces current sensor values and they occasional setting/friendly name message. Might consume settings to control its name and reporting frequency.<br />
<br />
===== Display unit =====<br />
Consumes:<br />
* Data produced by its associated sensor(s), which it displays.<br />
* Input from its associated manual input unit(s) <br />
* Settings to control what is associated with it etc.<br />
Produces:<br />
* Keepalives<br />
<br />
==== Power from the heater? ====<br />
As the heater on/of switch provides around 24V, maybe the controller can be run from that. Of course, if you complete the circuit with a short, the voltage is pulled down to 0, but maybe it is enough to use a pulldown, forming a voltage divider with whatever series R is used as a kind of pull up, but still enough for the heater to consider it a request for heat. <br />
<br />
Did some quick measurements to get an idea of what might be possible. At the Thermostat location in the living room, there is 22V available. When shorted with a 22kΩ resistor, this drops to 16.5V. So it seems that there is indeed some pull-up upstream.<br />
<br />
Assuming my multimeter is more or less perfect and no current of note runs when measuring voltage, and the cable used is also good, with no resistance of note, we have this voltage divider:<br />
<br />
0V---o---[22kΩ]---o---16.5V----[R]----22V<br />
<br />
So, 16.5V drops over 22kΩ, which means a current of (16.5/22)mA = 0.75mA flows (Ohm's law, I=U/R;R=U/I;U=RI). The same current flows through R, and 5.5V drops over R, so R is (5.5/0.75)kΩ = 7 1/3 kΩ<br />
<br />
If we drop the entire 22V over R alone, the expected current would be about 3mA, so this is probably the maximum we should try to draw. I checked, and indeed when short-circuiting with a multimeter, 3mA is exactly what's on the display.<br />
Anyway, 3mA at 22V is only 66mW, (22mA at 3V) so not a lot of power to work with, but might be enough for a small microprocessor. 'Real' thermostats use a kind of supercap to buffer energy.<br />
<br />
Next is to figure out what actually makes the heater switch on? If it is based on the voltage being drawn to (almost) 0, then bad luck. I suppose it is more likely what is being measured is current flowing through the thermostat, so any reasonable drop over R is enough. This kind of switch also has to work with less than perfect switches, after all. This might still mean bad luck, as it means we can't really pull much power from here, before triggering the heater...<br />
<br />
Next experiment would be putting in a variable resistor and see at what value of U/I the heater is triggered, but for now it seems a bit hopeless to use this idea. It does give us an idea of the max current a relay/FET/optocoupler would have to handle.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17781Luteijn/Thermostat2018-03-09T17:09:18Z<p>Luteijn: /* Basic idea */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does. Another option is a 24V tolerant optocoupler. I'll try that as it should take a lot less power than a relay, but still provide some isolation from the 24V. Ordered a batch of PC817's from China, thes should handle up to 50mA and 35V, which should be plenty (see under power from heater section)<br />
<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there. Might just skip this step and make a wireless bridge right away.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT. Voltage on the thermostat side seems to be available, so the ESP can use the Chronotherm for power. Would be nice to program it to be energy conservative.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* Remote controlled radiator(knobs) don't seem needed at first, just need to make sure the thermostatic knobs are set to match the desired max temperature, and trust the system to run the heater as long as at least one room needs heat. Eventually it would make things even better to have remote controlled radiators, so a room can be kept cooler than its max even if there is another room that needs to heat up. Should be a relatively simple operation to pop off the existing knobs and replace them by remote controlled ones that can be set to the desired temperature by the system, or just opened/closed more based on the heat demand from the room they are in.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== faceplate ====<br />
draft version [[file:Faceplate.svg]]<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|keepalive<br />
|state="alive"|"recovering"|"leaving"|"power"|<whatever><br />
|Periodic message to report (continued) presence, intention to leave, power issues (battery low etc.), and/or recovering from e.g. power outage, reset etc.<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Control node =====<br />
This will be located near a/the heater boiler/furnace (or airco, or ventilator or whatever) and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
* Desires and Engages directed to it<br />
<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit =====<br />
Basically just produces current sensor values and they occasional setting/friendly name message. Might consume settings to control its name and reporting frequency.<br />
<br />
===== Display unit =====<br />
Consumes:<br />
* Data produced by its associated sensor(s), which it displays.<br />
* Input from its associated manual input unit(s) <br />
* Settings to control what is associated with it etc.<br />
Produces:<br />
* Keepalives<br />
<br />
==== Power from the heater? ====<br />
As the heater on/of switch provides around 24V, maybe the controller can be run from that. Of course, if you complete the circuit with a short, the voltage is pulled down to 0, but maybe it is enough to use a pulldown, forming a voltage divider with whatever series R is used as a kind of pull up, but still enough for the heater to consider it a request for heat. <br />
<br />
Did some quick measurements to get an idea of what might be possible. At the Thermostat location in the living room, there is 22V available. When shorted with a 22kΩ resistor, this drops to 16.5V. So it seems that there is indeed some pull-up upstream.<br />
<br />
Assuming my multimeter is more or less perfect and no current of note runs when measuring voltage, and the cable used is also good, with no resistance of note, we have this voltage divider:<br />
<br />
0V---o---[22kΩ]---o---16.5V----[R]----22V<br />
<br />
So, 16.5V drops over 22kΩ, which means a current of (16.5/22)mA = 0.75mA flows (Ohm's law, I=U/R;R=U/I;U=RI). The same current flows through R, and 5.5V drops over R, so R is (5.5/0.75)kΩ = 7 1/3 kΩ<br />
<br />
If we drop the entire 22V over R alone, the expected current would be about 3mA, so this is probably the maximum we should try to draw. I checked, and indeed when short-circuiting with a multimeter, 3mA is exactly what's on the display.<br />
Anyway, 3mA at 22V is only 66mW, (22mA at 3V) so not a lot of power to work with, but might be enough for a small microprocessor. 'Real' thermostats use a kind of supercap to buffer energy.<br />
<br />
Next is to figure out what actually makes the heater switch on? If it is based on the voltage being drawn to (almost) 0, then bad luck. I suppose it is more likely what is being measured is current flowing through the thermostat, so any reasonable drop over R is enough. This kind of switch also has to work with less than perfect switches, after all. This might still mean bad luck, as it means we can't really pull much power from here, before triggering the heater...<br />
<br />
Next experiment would be putting in a variable resistor and see at what value of U/I the heater is triggered, but for now it seems a bit hopeless to use this idea. It does give us an idea of the max current a relay/FET/optocoupler would have to handle.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17780Luteijn/Thermostat2018-03-09T17:04:50Z<p>Luteijn: /* Power from the heater? */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* Remote controlled radiator(knobs) don't seem needed at first, just need to make sure the thermostatic knobs are set to match the desired max temperature, and trust the system to run the heater as long as at least one room needs heat. Eventually it would make things even better to have remote controlled radiators, so a room can be kept cooler than its max even if there is another room that needs to heat up. Should be a relatively simple operation to pop off the existing knobs and replace them by remote controlled ones that can be set to the desired temperature by the system, or just opened/closed more based on the heat demand from the room they are in.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== faceplate ====<br />
draft version [[file:Faceplate.svg]]<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|keepalive<br />
|state="alive"|"recovering"|"leaving"|"power"|<whatever><br />
|Periodic message to report (continued) presence, intention to leave, power issues (battery low etc.), and/or recovering from e.g. power outage, reset etc.<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Control node =====<br />
This will be located near a/the heater boiler/furnace (or airco, or ventilator or whatever) and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
* Desires and Engages directed to it<br />
<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit =====<br />
Basically just produces current sensor values and they occasional setting/friendly name message. Might consume settings to control its name and reporting frequency.<br />
<br />
===== Display unit =====<br />
Consumes:<br />
* Data produced by its associated sensor(s), which it displays.<br />
* Input from its associated manual input unit(s) <br />
* Settings to control what is associated with it etc.<br />
Produces:<br />
* Keepalives<br />
<br />
==== Power from the heater? ====<br />
As the heater on/of switch provides around 24V, maybe the controller can be run from that. Of course, if you complete the circuit with a short, the voltage is pulled down to 0, but maybe it is enough to use a pulldown, forming a voltage divider with whatever series R is used as a kind of pull up, but still enough for the heater to consider it a request for heat. <br />
<br />
Did some quick measurements to get an idea of what might be possible. At the Thermostat location in the living room, there is 22V available. When shorted with a 22kΩ resistor, this drops to 16.5V. So it seems that there is indeed some pull-up upstream.<br />
<br />
Assuming my multimeter is more or less perfect and no current of note runs when measuring voltage, and the cable used is also good, with no resistance of note, we have this voltage divider:<br />
<br />
0V---o---[22kΩ]---o---16.5V----[R]----22V<br />
<br />
So, 16.5V drops over 22kΩ, which means a current of (16.5/22)mA = 0.75mA flows (Ohm's law, I=U/R;R=U/I;U=RI). The same current flows through R, and 5.5V drops over R, so R is (5.5/0.75)kΩ = 7 1/3 kΩ<br />
<br />
If we drop the entire 22V over R alone, the expected current would be about 3mA, so this is probably the maximum we should try to draw. I checked, and indeed when short-circuiting with a multimeter, 3mA is exactly what's on the display.<br />
Anyway, 3mA at 22V is only 66mW, (22mA at 3V) so not a lot of power to work with, but might be enough for a small microprocessor. 'Real' thermostats use a kind of supercap to buffer energy.<br />
<br />
Next is to figure out what actually makes the heater switch on? If it is based on the voltage being drawn to (almost) 0, then bad luck. I suppose it is more likely what is being measured is current flowing through the thermostat, so any reasonable drop over R is enough. This kind of switch also has to work with less than perfect switches, after all. This might still mean bad luck, as it means we can't really pull much power from here, before triggering the heater...<br />
<br />
Next experiment would be putting in a variable resistor and see at what value of U/I the heater is triggered, but for now it seems a bit hopeless to use this idea. It does give us an idea of the max current a relay/FET/optocoupler would have to handle.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Vortex&diff=17779Luteijn/Vortex2018-03-08T07:25:41Z<p>Luteijn: </p>
<hr />
<div>===Project "Vortex":===<br />
{{Project<br />
|Name=Luteijn/Vortex<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=Vortex_1.jpg<br />
}}<br />
<br />
I've got four DFRobot 'Vortex' units. Basic programming via the official apps is possible, but rather limited. The WhenDo app is only available on iPad. Luckily, the internal Arduino clone can be directly programmed too. Unfortunately, not all the specs seem to be available, but there are some examples to work with, although they have a quite a bit of 'magic numbers' in them. These examples might disappear, so duplicating them here, with my own notes as available. The examples-coding is mostly done by "Andy Zhou <Andy.zhou@dfrobot.com>", although I did change some of the things to figure out what they do.<br />
<br />
http://wiki.dfrobot.com.cn/index.php?title=(SKU:ROB0116)_Vortex%E5%8F%AF%E7%BC%96%E7%A8%8B%E6%9C%BA%E5%99%A8%E4%BA%BA#.E6.A0.B7.E4.BE.8B.E4.BB.A3.E7.A0.81 <br />
<br />
https://www.dfrobot.com/wiki/index.php/Vortex_Arduino_Coding_Tutorial_V1.0#Introduction<br />
<br />
(Chinese page seems to have a bit better documentation, although English page might be easier to read and it is interesting to compare the differences)<br />
<br />
http://wiki.dfrobot.com.cn/images/1/10/%E4%B8%BB%E6%9D%BF.png<br />
<br />
==== What's connected to the pins ====<br />
{| class="wikitable"<br />
|+ Connection overview<br />
|-<br />
|0<br />
|Serial RX (input)<br />
|-<br />
|1<br />
|Serial TX (output)<br />
|-<br />
|2<br />
|Encoder Wheel (External Interrupt 0) (input)<br />
|-<br />
|3<br />
|Encoder Wheel (External Interrupt 1) (input)<br />
|-<br />
|4<br />
|Unknown, seems to be pulled high when used as input. Found it too hard to trace on the board.<br />
|-<br />
|5<br />
|Motor 0 Speed (PWM) (output)<br />
|-<br />
|6<br />
|Motor 1 Speed (PWM) (output)<br />
|-<br />
|7<br />
|IR decoder/detector (input)<br />
|-<br />
|8<br />
|IR led Left (output)<br />
|-<br />
|9<br />
|Motor 0 direction (H=forwards,L=reverse) (output)<br />
|-<br />
|10<br />
|Motor 1 direction (output)<br />
|-<br />
|11<br />
|MP3-player TX (output)<br />
|-<br />
|12<br />
|IR led Right (output)<br />
|-<br />
|13<br />
|GRB LED chain around body (output)<br />
|-<br />
|A0<br />
|grayscale 4 'D' (input)<br />
|-<br />
|A1<br />
|grayscale 3 'C' (input)<br />
|-<br />
|A2<br />
|grayscale 2 'B' (input)<br />
|-<br />
|A3<br />
|grayscale 1 'A' (input)<br />
|-<br />
|A4<br />
|SDA (i2c)<br />
|-<br />
|A5<br />
|SCL (i2c)<br />
|-<br />
|A6<br />
|grayscale 5 'E' (input)<br />
|-<br />
|A7<br />
|grayscale 6 'F' (input)<br />
|-<br />
|}<br />
<br />
i2c bus + Vcc & Gnd should be available at 4 pin rear expansion port too - need to find a suitable connector for it..<br />
<br />
http://www.hobbytronics.co.uk/arduino-atmega328-pinout Useful diagrams to map pins to package<br />
<br />
==== Dumping original firmware ====<br />
Although you can/should be able to reflash Vortex with the App, might as well make a backup of the orignal:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:r:"Vortex.hex":i<br />
This can be uploaded again with:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:w:"Vortex.hex":i<br />
(The app might also be initializing other things, but this seems to work)<br />
<br />
Your own sketches can be uploaded with <br />
arduino --upload MySketch.ino<br />
But using avrdude to upload the .hex file is a bit quicker.<br />
<br />
==== Motor Control ====<br />
<br />
Simple 'enable' and 'direction' pins for left and right wheels. Enable can be PWM'd or just full on.<br />
This example revs up the engines with PWM.<br />
<pre><br />
int E1 = 5; <br />
int M1 = 9; <br />
int E2 = 6; <br />
int M2 = 10; <br />
<br />
void setup() <br />
{ <br />
pinMode(M1, OUTPUT); // directional controls, High is forward, Low is backward <br />
pinMode(M2, OUTPUT); <br />
} <br />
<br />
void loop() <br />
{ <br />
int value; <br />
for(value = 0 ; value <= 255; value+=5) //foreward <br />
{ <br />
digitalWrite(M1,HIGH); <br />
digitalWrite(M2, HIGH); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
<br />
for(value = 0 ; value <= 255; value+=5) //backward <br />
{ <br />
digitalWrite(M1, LOW); <br />
digitalWrite(M2, LOW); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
} <br />
</pre><br />
<br />
====Encoders====<br />
The two external interrupts are linked to encoders that track wheel movement, so it is possible to detect the Vortext is being pushed/pulled/turned, although I don't think these things indicate which direction the robot is pushed in...<br />
<br />
<pre><br />
#define pinInputLeft 0 // this is the interrupt, not the pin number, D2<br />
#define pinInputRight 1 // external int 1, D3<br />
long leftPul,rightPul;<br />
<br />
void leftCallBack(){<br />
leftPul++;<br />
}<br />
<br />
void rightCallBack(){<br />
rightPul++;<br />
}<br />
<br />
void initDdevice(){<br />
pinMode(5,OUTPUT);<br />
pinMode(6,OUTPUT);<br />
pinMode(9,OUTPUT);<br />
pinMode(10,OUTPUT);<br />
noInterrupts();<br />
attachInterrupt(pinInputLeft,leftCallBack,CHANGE);<br />
attachInterrupt(pinInputRight,rightCallBack,CHANGE);<br />
interrupts();<br />
}<br />
<br />
void motorDebug(){<br />
digitalWrite(5,HIGH);<br />
digitalWrite(6,HIGH);<br />
digitalWrite(9,HIGH);<br />
digitalWrite(10,HIGH);<br />
}<br />
<br />
void printPul(){<br />
Serial.print(leftPul);<br />
Serial.print(" ");<br />
Serial.println(rightPul);<br />
leftPul = 0;<br />
rightPul = 0;<br />
}<br />
<br />
void setup() {<br />
initDdevice();<br />
Serial.begin(9600);<br />
motorDebug();<br />
}<br />
<br />
void loop() {<br />
printPul();<br />
delay(500);<br />
}<br />
</pre><br />
====Analogue Inputs====<br />
six 'grayscale' detectors are placed around the bottom of the Vortex, to be used to track a line, maybe read simple codes from the floor.<br />
<pre> <br />
2 (a2) 3 (a1) <br />
1 (a3) 4 (a0)<br />
<br />
<br />
<br />
<br />
5 (a6) 6 (a7)<br />
<br />
</pre><br />
This is the example sketch to dump the values read by the 6 little eyes.<br />
<pre><br />
void setup(void){<br />
Serial.begin(9600); <br />
}<br />
<br />
int analogBuf[6] = {'\0'};<br />
<br />
void loop(void){<br />
analogBuf[0] = analogRead(3);<br />
analogBuf[1] = analogRead(2);<br />
analogBuf[2] = analogRead(1);<br />
analogBuf[3] = analogRead(0);<br />
analogBuf[4] = analogRead(6);<br />
analogBuf[5] = analogRead(7);<br />
for(int i=0;i<6;i++){<br />
Serial.print(i+1);<br />
Serial.print(": ");<br />
Serial.print(analogBuf[i]);<br />
Serial.print(" ");<br />
}<br />
Serial.println();<br />
delay(500);<br />
}<br />
</pre><br />
<br />
Wonder what's connected to A4 and A5, if anything? These are used for the i2c bus! See below under the [[Luteijn/Vortex#Eyes]] section<br />
<br />
====LEDs====<br />
12 RGB (GRB!) leds can be addressed. Example from website causes Red and Green to be swapped. initialize library differently:<br />
<pre><br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
</pre><br />
IR example below uses some of the LED functionality.<br />
<br />
====IR obstacle detector====<br />
Seems to be somewhat flaky. Needs a bit more work to figure out. Also, the IR sensor is useful to read IR remote controllers or beacons. This receiver seems to work at 38kHz, but probabbly also reacts to 36-40kHz. Remote controllers often work at 36kHz. Note that 2x 8µs delay doesn't give you 38kHz, but the digitalWrite itself also induces quite some delay (3-6µs), so seems this is bringing the total period up to around the 26.something µs we'd expect.<br />
<br />
<br />
<pre><br />
#define NUM_LEDS 12 <br />
<br />
// Data pin that led data will be written out over <br />
#define DATA_PIN 13 <br />
CRGB leds[NUM_LEDS]; <br />
<br />
#define IR_IN 7//IR receiver pin <br />
#define L_IR 8 //left ir transmitter pin <br />
#define R_IR 12 //right ir transmitter pin <br />
<br />
int count; <br />
<br />
void leftSend38KHZ(void){//left ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(L_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(L_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
void rightSend38KHZ(void){//right ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(R_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(R_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
<br />
void pcint0Init(void){//init the interrupt <br />
PCICR |= 1 << PCIE2; <br />
PCMSK2 |= 1 << PCINT23; //pin D7 is int23 <br />
} <br />
<br />
ISR(PCINT2_vect){//IR decoder interrupt <br />
count++; <br />
} <br />
<br />
void obstacleAvoidance(void){ <br />
char i; <br />
count=0; <br />
leds[5] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){ //left transmitter sends 20 pulses <br />
leftSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[5] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Left"); <br />
delay(100); <br />
} <br />
count=0; <br />
leds[3] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){//right transmitter sends 20 pulses <br />
rightSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[3] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Right"); <br />
delay(100); <br />
} <br />
delay(600); <br />
} <br />
<br />
void setup(void){ <br />
delay(2000); <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
pinMode(L_IR,OUTPUT);//init the left transmitter pin <br />
pinMode(R_IR,OUTPUT);//init the right transmitter pin <br />
pinMode(IR_IN,INPUT);//init the ir receiver pin <br />
Serial.begin(9600); <br />
leds[4] = CRGB::Blue; <br />
FastLED.show(); <br />
delay(2000); <br />
leds[4] = CRGB::Purple; <br />
FastLED.show(); <br />
noInterrupts(); <br />
pcint0Init(); <br />
interrupts(); //enable the interrupt <br />
} <br />
<br />
void loop(void){ <br />
obstacleAvoidance(); <br />
}<br />
</pre><br />
<br />
==== IRreceiver ====<br />
Since DFrobot also have a 'loose' IR-receiver module in their program, decided to just see if the example sketches for those would transfer, and they do.<br />
https://www.dfrobot.com/product-366.html - A remote similar to the one pictured on the DFRobot product page actually came with one of my RTLSDR sticks, and seems to work well enough, although the range isn't great, couple of meters. With a 'proper' remote control, e.g. from a TV, the range is fine, 10m at least when pointing at unit from across the house. Might be less if using reflections to try to control a roving robot.<br />
<br />
So, using the IRremote library you can read values from a remote controller:<br />
<pre><br />
#include "IRremote.h"<br />
#define IR_IN 7//IR receiver pin<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
<br />
void setup(void){<br />
Serial.begin(9600); <br />
irrecv.enableIRIn(); // Start the receiver<br />
} <br />
<br />
void loop(void){ <br />
if (irrecv.decode(&results)) { <br />
Serial.println(results.value, HEX); <br />
irrecv.resume(); // Receive the next value<br />
} <br />
} <br />
</pre><br />
This was already useful to read codes from the remote of the AV-receiver we have at home and then play these back via a universal remote app on the smartphone that was missing some buttons. For some reason I couldn't find the 'learn' function in the app, but could enter the codes as read by this sketch easily.<br />
<br />
The receiver is mounted on the front, bottom. So not right between the eyes, although a spot seems to be prepared for it there too.<br />
<br />
The tables at [[Luteijn/IRRemotes]] might be useful when creating a sketch that is to be controlled over IR.<br />
<br />
Here's an example of a simple driving sketch controlled via my RTeL-Cheapo remote.<br />
<pre><br />
#include "IRremote.h"<br />
<br />
#define IR_IN (7)//IR receiver pin<br />
#define E1 (5)<br />
#define E2 (6)<br />
#define M1 (9)<br />
#define M2 (10)<br />
<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
// MSB is direction, 0xff full forward, 0x00 full backwards<br />
uint8_t Left=128; // 0x80 : forward, speed 0<br />
uint8_t Right=128; // 0x80 : forward, speed 0<br />
<br />
void setup(void){<br />
delay(2000); // grace period<br />
Serial.begin(9600);<br />
pinMode(M1, OUTPUT);<br />
pinMode(M2, OUTPUT);<br />
Engine(Left,Right);<br />
irrecv.enableIRIn(); // Start the receiver<br />
}<br />
<br />
void Engine(uint8_t Left, uint8_t Right) {<br />
uint8_t D1=Left&0x80;<br />
uint8_t D2=Right&0x80;<br />
uint8_t S1=D1?Left&0x7f:0x7f-(Left&0x7f);<br />
uint8_t S2=D2?Right&0x7f:0x7f-(Right&0x7f);<br />
Serial.print(Left,HEX);<br />
Serial.print("<-L R->");<br />
Serial.println(Right,HEX);<br />
digitalWrite(M1,D1);<br />
analogWrite(E1,S1<<1);<br />
digitalWrite(M2,D2);<br />
analogWrite(E2,S2<<1);<br />
}<br />
uint32_t last;<br />
void loop(void){<br />
uint32_t code;<br />
if (code=irrecv.decode(&results)) {<br />
code=results.value;<br />
Serial.print("Got IR:");<br />
Serial.println(code,HEX);<br />
<br />
if (code==0xFFFFFFFF) {code=last;}; // repeated press<br />
<br />
if (code==0xFFB24D) { // power = full stop<br />
Left=128;<br />
Right=128;<br />
} else if (code==0xFF02FD) { // full screen both go towards full stop<br />
if (Left<128) Left++; else Left--;<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFFA05F) { // ch+ increase both towards full ahead<br />
if (Left<255) Left++;<br />
if (Right<255) Right++;<br />
} else if (code==0xFF40BF) { // ch- decrease both towards full reverse<br />
if (Left>0) Left--;<br />
if (Right>0) Right--;<br />
} else if (code==0xFF50AF) { // vol- left towards full stop<br />
if (Left<128) Left++; else Left--;<br />
} else if (code==0xFF32CD) { // record increase left towards full ahead<br />
if (Left<255) Left++;<br />
} else if (code==0xFF48B7) { // 0 decrease left towards full reverse<br />
if (Left>0) Left--;<br />
} else if (code==0xFF7887) { // vol+ right towards full stop<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFF30CF) { // time shift increase right towards full ahead<br />
if (Right<255) Right++;<br />
} else if (code==0xFF38C7) { // recall decrease right towards full reverse<br />
if (Right>0) Right--;<br />
} else {<br />
// ignore unknown codes<br />
}<br />
last=code;<br />
irrecv.resume(); // Receive the next value<br />
Engine(Left,Right);<br />
}<br />
}<br />
</pre><br />
It would be better to use a stronger remote for this as it's hard to successfully control the robot when it's facing away from you. Also, the speed ramp up/down is a bit slow, and should probably take bigger steps than one at a time. <br />
<br />
Left as an exercise to the reader: add controls to spin in place left/right, using interrupts of wheel encoders to rotate 45/90 etc. degrees, boundary detection with analogue sensors, object detection with IR (careful not to make control harder). Switch to a bluetooth remote controller once figured out the possibilities of bluetooth better?<br />
<br />
====MP3-player====<br />
There is an MP3.player integrated in the robot. It can be controlled by sending more or less magic commands over software Serial via pin 11. Pin 2 might be connected to the player too if the example I found can be believed, but my tests so far never had anything received there, or on pin 4. Would be nice to get an 'end of song reached' or to get things like version information out. Anyway, Pin 2 is connected to one of the wheel encoders, so not likely to be the RX. It might also be co-connected to one of the relatively harmless outputs, like to the ir emitter? Will need to trace the board to find out, but initial quick look didn't immediately show anything.<br />
<br />
The example found on the DFRobot site:<br />
<pre><br />
#define MP3_VOLUME 0x10<br />
#define TX 11<br />
#define RX 4 // ?? was 2 in example I found, but that is unlikely<br />
#include <SoftwareSerial.h><br />
<br />
SoftwareSerial mySerial(RX, TX);// RX, TX<br />
void setup()<br />
{<br />
delay(1000); <br />
mp3Init();<br />
mp3setVolume(30);//0~30<br />
}<br />
void loop()<br />
{<br />
mp3player(1);<br />
delay(2000);<br />
mp3stop();<br />
delay(1000);<br />
}<br />
void mp3Init()<br />
{<br />
mySerial.begin (9600);<br />
}<br />
void mp3setVolume(byte vol)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
void mp3player(byte data)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
<br />
void mp3stop()<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x16, 0x00, 0x00, 0x00, 0xef};<br />
mySerial.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
The files to play can be put on the Vortex via usb, seems the little switch next to the micro-usb socket switches this between the mp3-players mass-storage and the arduino usb-serial interface. The number passed to the player is just the index into the (FAT?) table of stored songs. So, be careful of the order these are uploaded to the memory in. <br />
<br />
https://www.dfrobot.com/product-1121.html , documented at https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299 is a mini-MP3 player from DFRobot. It is controllable over serial, and might be the same one as used in the Vortex. Would be good to figure out if the 'busy' signal and/or the serial TX are connected back to the arduino somewhere in that case. Player could also be a variant but the magic numbers more or less match (although the stop command 0x16 is not in the table. <br />
<br />
The MP3-chip inside Vortex has Part# YX6100-24SS. Seems to be part of a family of serial MP3-players made by "广州悦欣电子科技有限公司" --> "Guangzhou Yue Xin Electronic Technology Co., Ltd." - website http://www.yx080.com/mp3xinpian/53-15.html has a download link on this yx6100 page but it seems to just refer us to the 5200 application Manual V1.8, which appears to be mostly compatible. The command table for the chip does include a 0x16 stop command. <br />
<br />
Also the datasheet mentions the chip can play both MP3 and WAV. YX5300-24SS is a similar part, datasheet at https://xp-dev.com/trac/arduino_antonio/export/280/arduino_antonio/trunk/EnterpriseBase/Serial%20MP3%20Player/About%20the%20Chip%20-%20YX5300/YX5300-24SS%20Datasheet%20V1.0.pdf The command table for that chip does include a 0x16 stop command.<br />
<br />
Documentation for the DFRobot miniplayer mentions the instruction format to be:<br />
{| class="wikitable"<br />
|+ Format: $S VER Len CMD Feedback para1 para2 checksum $O<br />
|-<br />
| $S<br />
| Start byte 0x7e<br />
| Each command begins with 0x7e ('~')<br />
|-<br />
| VER<br />
| Version information<br />
| Version seems to be 0xff from the example<br />
|-<br />
| Len<br />
| the number of bytes <br />
| Start, End and Checksum are not counted,<br />
|-<br />
| CMD<br />
| Commands<br />
| See command table.<br />
|-<br />
| Feedback<br />
| Command feedback<br />
| 1: feedback, 0: no feedback ; need to test what this does, might mean 'echo'<br />
|-<br />
| Para1<br />
| Parameter 1<br />
| Query high data byte<br />
|-<br />
| Para2<br />
| Parameter 2<br />
| Query low data byte<br />
|-<br />
| checksum<br />
| Checksum<br />
| accumulation and verification, doesn't include start byte. Seems to be 2 bytes from the example given in the docs, but Vortex seems to just not need it put in. example given is 7e ff 06 09 00 00 04 ff dd ef ; The 5200 Datasheet uses the exact same example command, and explains that you should add all the bytes, and then subtract those from 0 to get the checksum. The 5300 Datasheet also mentions "另外用户也可以直接忽视校验,参考我们的5.3.4 章节说明。" so looks like the checksum is optional if you don't care too much about corruption of the occasional command.<br />
|-<br />
| $O<br />
| End byte<br />
| End of command is signaled with 0xEF<br />
|}<br />
<br />
{| class="wikitable"<br />
|+Command table WIP to copy this over<br />
|-<br />
!CMD<br />
!Function Description<br />
!Parameters (16 bit)<br />
!compared with YX5300 info<br />
|-<br />
|0x01<br />
|Next<br />
|<br />
|-<br />
|0x02<br />
|Previous<br />
|<br />
|-<br />
|0x03<br />
|Specific track<br />
|0-2999 but example is only using low byte. also track 0 doesn't seem to do anything?<br />
|Indicates 1-255 are the valid tracks only.<br />
|-<br />
|0x04<br />
|Volume up<br />
|<br />
|-<br />
|0x05<br />
|Volume down<br />
|<br />
|-<br />
|0x06<br />
|Set Volume<br />
|0-30 (10 is already quite loud!)<br />
|-<br />
|0x07<br />
|Specify EQ(0/1/2/3/4/5/)<br />
|Normal/Pop/Rock/Jazz/Classic/Base.<br />
|Reserved in the 5300's command list.<br />
|-<br />
|0x08<br />
|Specify playback mode (0/1/2/3)<br />
|Repeat/folder repeat/single repeat/random<br />
|See 3.4.3; this explains the argument is the song to play in a loop - may have been changed for 6100<br />
|-<br />
|0x09<br />
|Select source (0/1/2/3/4)<br />
|U/TF/AUX/SLEEP/FLASH<br />
|See 3.4.4; specifies 1 as U, 2 as TF, 4 as PC, 5 FLASH and 6 SLEEP<br />
|-<br />
|0x0a<br />
|Enter into standby - low power loss<br />
|<br />
|Sleep - Low power consumption 10MA (obviously mA or µA, not MA, is meant)<br />
|-<br />
|0x0b<br />
|Normal working<br />
|<br />
|Wake up from sleep<br />
|-<br />
|0x0c<br />
|reset module<br />
|<br />
|chip reset<br />
|-<br />
|0x0d<br />
|Playback<br />
|<br />
|Play - seems to be contrast to suspend/pause<br />
|-<br />
|0x0e<br />
|Pause<br />
|<br />
|Suspended<br />
|-<br />
|0x0f<br />
|Specify folder to playback<br />
|1~10(need to set by user)<br />
|see 3.4.5 - DH: represents the name of the folder, the default support for 99 files, 01 - 99 named DL: represents the track, the default maximum of 255 songs, that is, 0x01 ~ 0xFF<br />
|-<br />
|0x10<br />
|Volume adjust set<br />
|DH=1:open volume adjust DL: set volume gain 0~31<br />
|not present in command table <br />
|-<br />
|0x11<br />
|Repeat play<br />
|1:start repeat play 0: stop play<br />
|not present in command table<br />
|-<br />
|0x16<br />
|not in the table<br />
|example mentions it as a stop command<br />
|Stop<br />
|-<br />
|0x17<br />
|not in the table<br />
|<br />
|FLASH only, see 3.4.7 (but is in section 3.4.6), select folder to loop.<br />
|-<br />
|0x18<br />
|not in the table<br />
|<br />
|Reserved<br />
|-<br />
|0x19<br />
|not in the table<br />
|<br />
|See 3.4.8 (but is in section 3.4.7), select loop current track mode<br />
|-<br />
|0x21<br />
|not in the table<br />
|<br />
|See 3.4.9 (3.4.8), set DAC to High-Z mode (1) or on (0) so you can use the amp for something else.<br />
|-<br />
|0x22<br />
|not in the table<br />
|<br />
|See 3.4.10 (section 3.4.9 doesn't exist), Set volume and song to play in one command (H:volume L:track)<br />
|-<br />
|0x3C<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3D<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3E<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3F<br />
|Send initialization parameters<br />
|0-0x0F (each bit represent one device of the low-four bits)<br />
|Seems to be reporting which storage devices are currently online (see section 3.5.1) <br />
|-<br />
|0x40<br />
|Returns an error, request retransmission<br />
|<br />
|Probably this is a response to a command meaning Error Encountered.<br />
|-<br />
|0x41<br />
|Answer<br />
|<br />
|Probably this is a response to a query and just means Accepted.<br />
|-<br />
|0x42<br />
|Query Status<br />
|<br />
|See 3.4.10 (actually 3.5.2) - explains that this will return which storage is used and if it's playing, stopped or paused. The storage could also be 'SLEEP' to indicate sleeping?<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
<br />
|}<br />
<br />
Code to calculate the checksum from the Library supporting the miniplayer:<br />
<pre><br />
uint16_t DFRobotDFPlayerMini::calculateCheckSum(uint8_t *buffer){<br />
uint16_t sum = 0;<br />
for (int i=Stack_Version; i<Stack_CheckSum; i++) {<br />
sum += buffer[i];<br />
}<br />
return -sum;<br />
}<br />
</pre><br />
but this doesn't seem to be needed.<br />
<br />
====Eyes====<br />
The 'Eyes' are connected over i2c. The daughterboard they are on features an STM8S103K3 (see http://www.st.com/en/microcontrollers/stm8s103-105.html ) and 2x HC595 (shift registers).<br />
Besides 35 predefined eye patterns, it is also possible to upload your own eye patterns.<br />
<br />
The default eyes can be set as follows:<br />
<pre><br />
#include <Wire.h><br />
#define I2C_LED_ADDRESS 0b1100000<br />
#define I2C_WRITE 0x00<br />
<br />
uint8_t serial=0;<br />
<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
defined_eyes(6,1); <br />
delay(2000);<br />
}<br />
void loop(){<br />
defined_eyes(1,serial); <br />
serial++; <br />
if(serial>=35) serial=0; <br />
delay(250); <br />
}<br />
<br />
<br />
void defined_eyes(uint8_t color,uint8_t serial) {<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4 <br />
// (I suppose they mean 0x40, although eyes seem to also respond on 0xC0 which is what is defined above, and 0x20)<br />
Wire.write(color&0x07); // color bits: 1 blue, 2 green, 4 red <br />
Wire.write(serial); //preset eyes 0~34<br />
Wire.endTransmission(); // stop transmitting <br />
}<br />
<br />
</pre><br />
<pre><br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21 <br />
//6 7 8 9 10 20 19 18 17 16 <br />
//11 12 13 14 15 15 14 13 12 11 <br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1 <br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4<br />
Wire.write(0x55); // color 55=custom eyes follow <br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
// other values cause eyes to light up in multiple colors, needs more work<br />
<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R of foreground<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
} <br />
Wire.endTransmission(); // stop transmitting <br />
<br />
<br />
void example_eyes() {<br />
/* left eye of robot (right for onlooker) */<br />
Wire.write(0x1f); // bottom row, all bits lit<br />
Wire.write(0x1e); // 10 9 8 7 on, 6 off<br />
Wire.write(0x1c);<br />
Wire.write(0x18);<br />
Wire.write(0x10);<br />
<br />
/* right eye of robot (left for onlooker) */<br />
Wire.write(0x1f); // top row all 5 bits lit<br />
Wire.write(0x0f); // leds 6,7,8,9 on 10 off<br />
Wire.write(0x07); // 11,12,13 on, 14,15 off <br />
Wire.write(0x03); <br />
Wire.write(0x01); <br />
} <br />
</pre><br />
<br />
=====KITT-scanner / Cylon=====<br />
This animates the eyes and adds some simple sound effects - currently the eye warble is not synchronized to the eye movement. The IR remote is used to trigger the well-known 'By your command'. For a KITT scanner may want to change to the proper woosh for a KITT, and replace the command sample with something like 'yes, Michael' :)<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <SoftwareSerial.h><br />
#include <IRremote.h><br />
<br />
#define MTX (11)<br />
#define MRX (4)<br />
<br />
// Song number for "By your command"-sample<br />
#define BYC (30)<br />
// Song number for Warble<br />
#define Warble (31)<br />
<br />
#define IR_IN (7)<br />
<br />
#define I2C_LED_ADDRESS 0x80<br />
bool left=1;<br />
decode_results results;<br />
SoftwareSerial mp3(MRX,MTX);<br />
IRrecv irrecv(IR_IN);<br />
short command=0;<br />
<br />
uint8_t eyes[10]={0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00};<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
mp3Init();<br />
irrecv.enableIRIn(); // Start the receiver<br />
delay(2000);<br />
delay(100);<br />
mp3setVolume(10);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(2); // repeat once<br />
delay(50);<br />
mp3player(BYC);<br />
delay(3000);<br />
mp3setVolume(8);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
delay(50);<br />
}<br />
void lscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]<<1);<br />
eyes[7]=(uint8_t)(eyes[7]<<1);<br />
}<br />
<br />
void rscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]>>1);<br />
eyes[7]=(uint8_t)(eyes[7]>>1);<br />
}<br />
<br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
<br />
// 1 2 4 8 10 '20' 10 8 4 2 1<br />
if (left) {lscroll_eyes(eyes);}<br />
else if (!left) {rscroll_eyes(eyes);}<br />
<br />
custom_eyes(4,eyes);<br />
<br />
if (eyes[7]==0x1) {left=1;}<br />
else if (eyes[2]==0x1) {left=1;}<br />
else if (left && eyes[2]>=0x10) {eyes[2]=0;eyes[7]=0x20;left=0;}<br />
else if (left && eyes[7]>=0x10) {eyes[7]=0;eyes[2]=0x20;left=0;}<br />
if (irrecv.decode(&results)){<br />
if (command) {<br />
command=0;<br />
} else{<br />
command=34; // adjust to match sample length<br />
mp3player(BYC);<br />
delay(50);<br />
}<br />
irrecv.resume();<br />
} else {<br />
if (command) {<br />
command--;<br />
if (!command){<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
} else {<br />
delay(80);<br />
}<br />
} else {<br />
delay(80);<br />
}<br />
}<br />
}<br />
<br />
void mp3Init()<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x0C, 0x00, 0x00, 0x00, 0xef};<br />
mp3.begin(9600);<br />
delay(100);<br />
mp3.write(buffer, 8);<br />
delay(100);<br />
}<br />
void mp3setVolume(byte vol)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3player(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3repeat(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x08, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
===== Conway's Game of Life =====<br />
2 5x5 fields linked as a torus of 10x5. Change mapping of cells in the edge to make e.g. a front and a back side, or change to 8x8 and add 4 more faces and a bit more mapping to create an LED cube of 6 faces. Simple animation on the 2 eye matrices. This version doesn't actually do something with the remote control, but it was easy to add things like reset board, change color, introduce disturbance etc.<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <IRremote.h><br />
<br />
#define IR_IN (7)<br />
#define I2C_LED_ADDRESS 0x80<br />
<br />
// 5x5 matrix eye<br />
#define CX (5)<br />
#define CY (5)<br />
// edge of 1 cell on each end<br />
#define CXM (7)<br />
#define CYM (7)<br />
<br />
#define F (2) // 2 faces have I.<br />
<br />
decode_results results;<br />
IRrecv irrecv(IR_IN);<br />
<br />
uint8_t eyes[10];<br />
<br />
uint8_t board_a[F][CYM][CXM];<br />
uint8_t board_b[F][CYM][CXM];<br />
<br />
<br />
inline void clear(uint8_t board[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
for (k=0;k<F;k++) {<br />
for (j=0;j<CYM;j++) {<br />
for (i=0;i<CXM;i++) {<br />
board[k][j][i]=0;<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t neighbors(uint8_t s[][CYM][CXM], uint8_t k,uint8_t j,uint8_t i) {<br />
return s[k][j-1][i-1]+s[k][j-1][i]+s[k][j-1][i+1]+s[k][j][i-1]+s[k][j][i+1]+s[k][j+1][i-1]+s[k][j+1][i]+s[k][j+1][i+1];<br />
}<br />
<br />
inline void play(uint8_t s[][CYM][CXM], uint8_t d[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
// TODO: load edges from right faces hardcoded for 5x5 now<br />
for (i=1;i<=5;i++) {<br />
s[0][0][i]=s[0][5][i];<br />
s[0][6][i]=s[0][1][i];<br />
<br />
s[0][i][0]=s[1][i][5];<br />
s[0][i][6]=s[1][i][1];<br />
<br />
s[1][0][i]=s[1][5][i];<br />
s[1][6][i]=s[1][1][i];<br />
<br />
s[1][i][0]=s[0][i][5];<br />
s[1][i][6]=s[0][i][1];<br />
<br />
}<br />
// corners<br />
s[0][0][0]=s[1][5][5];<br />
s[0][6][0]=s[1][1][5];<br />
<br />
s[0][0][6]=s[1][5][1];<br />
s[0][6][6]=s[1][1][1];<br />
<br />
s[1][0][0]=s[0][5][5];<br />
s[1][6][0]=s[0][1][5];<br />
<br />
s[1][0][6]=s[0][5][1];<br />
s[1][6][6]=s[0][1][1];<br />
<br />
<br />
// update interior cells<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
switch (neighbors(s,k,j,i)) {<br />
case 2: d[k][j][i]=s[k][j][i]; // remain<br />
break;<br />
case 3: d[k][j][i]=1; // birth<br />
break;<br />
default: d[k][j][i]=0; // death<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t convert_l(uint8_t l[CXM]) {<br />
return (l[5]|l[4]<<1|l[3]<<2|l[2]<<3|l[1]<<4);<br />
}<br />
inline uint8_t convert_r(uint8_t l[CXM]) {<br />
return (l[1]|l[2]<<1|l[3]<<2|l[4]<<3|l[5]<<4);<br />
}<br />
<br />
inline uint8_t * board_to_eyes(uint8_t board[F][CYM][CXM]) {<br />
static uint8_t eyes[10]={0,0,0,0,0,0,0,0,0,0};<br />
eyes[0]=convert_l(board[1][5]);<br />
eyes[1]=convert_l(board[1][4]);<br />
eyes[2]=convert_l(board[1][3]);<br />
eyes[3]=convert_l(board[1][2]);<br />
eyes[4]=convert_l(board[1][1]);<br />
<br />
eyes[5]=convert_r(board[0][1]);<br />
eyes[6]=convert_r(board[0][2]);<br />
eyes[7]=convert_r(board[0][3]);<br />
eyes[8]=convert_r(board[0][4]);<br />
eyes[9]=convert_r(board[0][5]);<br />
<br />
return eyes;<br />
}<br />
<br />
<br />
void dump(uint8_t b[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
Serial.println("==");<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
Serial.print(b[k][j][i]?'x':'o');<br />
}<br />
Serial.println("");<br />
} <br />
Serial.println("==");<br />
}<br />
<br />
}<br />
<br />
void setup(){<br />
delay(2000);<br />
Serial.begin(9600);<br />
Serial.println("life");<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
irrecv.enableIRIn(); // Start the receiver<br />
clear(board_a); // start with empty board a<br />
clear(board_b); // start with empty board b<br />
//some initial state:<br />
board_a[0][1][2]=1;<br />
board_a[0][2][3]=1;<br />
board_a[0][3][1]=1;<br />
board_a[0][3][2]=1;<br />
board_a[0][3][3]=1;<br />
board_a[1][1][2]=1;<br />
board_a[1][2][3]=1;<br />
board_a[1][3][1]=1;<br />
board_a[1][3][2]=1;<br />
board_a[1][3][3]=1;<br />
Serial.println("inited");<br />
}<br />
<br />
inline void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21<br />
//6 7 8 9 10 20 19 18 17 16<br />
//11 12 13 14 15 15 14 13 12 11<br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
// dump(board_a);<br />
custom_eyes(4,board_to_eyes(board_a));<br />
play(board_a,board_b);<br />
delay(50);<br />
// dump(board_b);<br />
custom_eyes(4,board_to_eyes(board_b));<br />
play(board_b,board_a);<br />
delay(50);<br />
<br />
//TODO: add something to react to remote here, e.g. set some cells etc.<br />
if (irrecv.decode(&results)){<br />
irrecv.resume();<br />
} else {<br />
}<br />
}<br />
<br />
</pre><br />
<br />
===== Countdown Timer =====<br />
<br />
This sketch turns Vortex into an eggtimer to be used to keep track of how much time you have left when giving a talk, or before your food is ready. It displays the time left on the eyes, and can be controlled with a IR remote to (re)set the timer. LED colors are adjusted based on the time left, with vortex blinking red when you're out of time. <br />
<br />
Below the timer is set up to count minutes, but you can of course change the number of milliseconds per 'tick' to count seconds or whatever. Adding things like rotating lights to indicate progress of time or playing sound effects if desired are left to the reader, this is more or less a demo of using the eyes as a digit display. <br />
<br />
Because the two eyes are rotated 180 degrees compared to each other, there is code to swap bit-patterns (for the full 5 pixels per row, even if the digits here only use 3 per row). The font is rotated at run-time in setup(), so it's easier to tweak the font. <br />
<br />
Make sure to restart Vortex in time for the 55 day limit on the millis() roll-over not to affect things, or add code to detect and handle the rollover.<br />
<br />
The code to work with a remote is set up for a specific remote controller (RC 240 in TV mode), so you probably have to adjust this bit to work with the controller you intend to use.<br />
<br />
<pre><br />
#include <Wire.h> <br />
#include <IRremote.h> <br />
#include <FastLED.h> <br />
//IR receiver pin <br />
#define IR_IN (7) <br />
#define I2C_LED_ADDRESS 0b100000 <br />
#define I2C_WRITE 0x00 <br />
// How many leds are in the strip? <br />
#define NUM_LEDS 12 <br />
// Data pin that led data will be written out over <br />
#define DATA_PIN 13 <br />
CRGB leds[NUM_LEDS]; <br />
// how many milliseconds per count (1000=count seconds, 60000 count minutes) <br />
unsigned long MperC=60000; <br />
<br />
unsigned long start; <br />
short countdown; <br />
short displayed; <br />
uint8_t state=0; <br />
unsigned long m_elapsed; <br />
short c_elapsed; <br />
unsigned long long current=0xffffffff; <br />
unsigned long long last=0xffffffff; <br />
<br />
IRrecv irrecv(IR_IN); <br />
decode_results results; <br />
<br />
uint8_t eyes[10]={0,0,0,0,0,0,0,0,0,0}; <br />
<br />
uint8_t left_digit[10][5]={ <br />
{0x04,0x0A,0x0A,0x0A,0x04}, //0 <br />
{0x0E,0x04,0x04,0x0C,0x04}, //1 <br />
{0x0E,0x08,0x04,0x02,0x0C}, //2 <br />
{0x0C,0x02,0x0C,0x02,0x0C}, //3 <br />
{0x02,0x02,0x0E,0x0A,0x0A}, //4 <br />
{0x0E,0x02,0x0C,0x08,0x0E}, //5 <br />
{0x04,0x0A,0x0C,0x08,0x04}, //6 <br />
{0x04,0x04,0x02,0x02,0x0C}, //7 <br />
{0x04,0x0A,0x04,0x0A,0x04}, //8 <br />
{0x04,0x02,0x06,0x0A,0x04} //9 <br />
}; <br />
uint8_t right_digit[10][5]; <br />
uint8_t swap(uint8_t l) { <br />
switch (l&0x1f){ <br />
case 0x0: return 0; <br />
case 0x01: return 0x10; <br />
case 0x02: return 0x08; <br />
case 0x03: return 0x18; <br />
case 0x04: return 0x04; <br />
case 0x05: return 0x14; <br />
case 0x06: return 0x0C; <br />
case 0x07: return 0x1C; <br />
case 0x08: return 0x02; <br />
case 0x09: return 0x12; <br />
case 0x0a: return 0x0A; <br />
case 0x0b: return 0x1A; <br />
case 0x0c: return 0x06; <br />
case 0x0d: return 0x16; <br />
case 0x0e: return 0x0E; <br />
case 0x0f: return 0x1E; <br />
case 0x10: return 0x01; <br />
case 0x11: return 0x11; <br />
case 0x12: return 0x09; <br />
case 0x13: return 0x19; <br />
case 0x14: return 0x05; <br />
case 0x15: return 0x15; <br />
case 0x16: return 0x0D; <br />
case 0x17: return 0x1d; <br />
case 0x18: return 0x03; <br />
case 0x19: return 0x13; <br />
case 0x1a: return 0x0B; <br />
case 0x1b: return 0x1B; <br />
case 0x1c: return 0x07; <br />
case 0x1d: return 0x17; <br />
case 0x1e: return 0x0F; <br />
case 0x1f: return 0x1F; <br />
default: return 0; <br />
} <br />
} <br />
void setup(){ <br />
char i,j; <br />
delay(2000); <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
for (i=0;i<10;i++){ <br />
for (j=0;j<5;j++) { <br />
right_digit[i][j]=swap(left_digit[i][4-j]); <br />
} <br />
} <br />
irrecv.enableIRIn(); <br />
Wire.begin(); // join i2c bus (address optional for master) <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
for (i=0;i<12;i++){ <br />
leds[i]=CRGB::Green; <br />
} <br />
FastLED.show(); <br />
start=millis(); <br />
displayed=100; <br />
countdown=99; <br />
state=0; <br />
}<br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){ <br />
uint8_t index; <br />
// right eye left eye <br />
//1 2 3 4 5 25 24 23 22 21 <br />
//6 7 8 9 10 20 19 18 17 16 <br />
//11 12 13 14 15 15 14 13 12 11 <br />
//16 17 18 19 20 10 9 8 7 6 <br />
//21 22 23 24 25 5 4 3 2 1 <br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4 <br />
Wire.write(0x55); // color 55=custom eyes follow <br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line <br />
Wire.write(color&0x7); //color 1-B,2-G,4-R <br />
<br />
for (index=0;index<10;index++) { <br />
Wire.write(eyeline[index]); <br />
} <br />
Wire.endTransmission(); // stop transmitting <br />
// A9 = some sort of multicolor mode <br />
// AA = one color byte, then 10 bytes defining the 2 eyes. <br />
// AB = some sort of multicolor mode <br />
} <br />
void update_display(short displayed) { <br />
byte h=displayed/10; <br />
byte l=displayed%10; <br />
eyes[0]=left_digit[l][0]; <br />
eyes[1]=left_digit[l][1]; <br />
eyes[2]=left_digit[l][2]; <br />
eyes[3]=left_digit[l][3]; <br />
eyes[4]=left_digit[l][4]; <br />
eyes[5]=right_digit[h][0]; <br />
eyes[6]=right_digit[h][1]; <br />
eyes[7]=right_digit[h][2]; <br />
eyes[8]=right_digit[h][3]; <br />
eyes[9]=right_digit[h][4]; <br />
if (displayed<5) { <br />
custom_eyes(4,eyes); <br />
} else { <br />
custom_eyes(2,eyes); <br />
} <br />
} <br />
<br />
void update_leds(short displayed) { <br />
char i; <br />
if (displayed>5) { <br />
for (i=0;i<12;i++){ <br />
leds[i]=CRGB(0,displayed*2,0); <br />
} <br />
} else if (displayed>0) { <br />
for (i=0;i<12;i++){ <br />
leds[i]=0xff9900; <br />
} <br />
} else if (state==1) { <br />
for (i=0;i<6;i++) { <br />
leds[i]=CRGB::Red; <br />
leds[i+6]=CRGB::Black; <br />
} <br />
state=0; <br />
} else if (state==0) { <br />
for (i=0;i<6;i++) { <br />
leds[i+6]=CRGB::Red; <br />
leds[i]=CRGB::Black; <br />
} <br />
state=1; <br />
} <br />
FastLED.show(); <br />
}<br />
void loop(){ <br />
unsigned long m_elapsed=millis()-start; <br />
// FIXME handle overflow of millis. <br />
short c_elapsed=(m_elapsed/MperC); <br />
if (displayed>0) { <br />
if (countdown-c_elapsed!=displayed) { <br />
displayed=countdown-c_elapsed; <br />
displayed=displayed<0?0:displayed; <br />
update_display(displayed); <br />
} <br />
} <br />
update_leds(displayed); <br />
//TODO check IR commands <br />
if (irrecv.decode(&results)) { <br />
current=results.value; <br />
if (current!=last) { <br />
last=current; // store current key to be able to recognize repeats <br />
current=current&0xfffff7ff; // filter out repeat bit. This is for RC5. <br />
<br />
if (current==0xa) { // 'clear' on my particular remote <br />
countdown=0; <br />
} else if (current==0xf) { // 'memo' <br />
start=millis(); // reference time is now <br />
displayed=99; // restarts timer with current countdown setting <br />
} else if (current==0x0) { // 'digit 0', not all remotes might be this regular <br />
digit(0); <br />
} else if (current==0x1) { <br />
digit(1); <br />
} else if (current==0x2) { <br />
digit(2); <br />
} else if (current==0x3) { <br />
digit(3); <br />
} else if (current==0x4) { <br />
digit(4); <br />
} else if (current==0x5) { <br />
digit(5); <br />
} else if (current==0x6) { <br />
digit(6); <br />
} else if (current==0x7) { <br />
digit(7); <br />
} else if (current==0x8) { <br />
digit(8); <br />
} else if (current==0x9) { <br />
digit(9); <br />
} else if (current==0x0) { // TODO add pause, up/down by 1 <br />
} <br />
} <br />
irrecv.resume(); // Receive the next value <br />
} <br />
<br />
delay(500); <br />
} <br />
void digit(byte d) { <br />
displayed=0; // stops timer updates to display <br />
countdown=(countdown%10)*10+d; <br />
update_display(countdown); <br />
}<br />
<br />
</pre><br />
<br />
==== i2c ====<br />
There might be more stuff connected to the i2c already, I plan to do some tests to find out. Would be nice if e.g. the flash of the mp3 player or some other expansion was already there.<br />
<br />
Regardless of what's there already, Vortex seems to be designed to be extended over i2c, and could be a nice platform to do some experiments/demo's with. <br />
<br />
Here's a simple scanning sketch, looks like only 0x40 has something connected, and that would be the eye-display.<br />
<br />
<pre><br />
#include <Wire.h><br />
<br />
<br />
void setup()<br />
{<br />
Wire.begin();<br />
<br />
Serial.begin(9600);<br />
while (!Serial); // Leonardo: wait for serial monitor<br />
Serial.println("\nI2C Scanner");<br />
}<br />
<br />
<br />
void loop()<br />
{<br />
byte error, address;<br />
int nDevices;<br />
<br />
Serial.println("Scanning...");<br />
<br />
nDevices = 0;<br />
for(address = 0; address < 128; address++ )<br />
{<br />
// The i2c_scanner uses the return value of<br />
// the Write.endTransmisstion to see if<br />
// a device did acknowledge to the address.<br />
Wire.beginTransmission(address);<br />
error = Wire.endTransmission();<br />
<br />
if (error == 0)<br />
{<br />
Serial.print("I2C device found at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
<br />
nDevices++;<br />
}<br />
else if (error==4)<br />
{<br />
Serial.print("Unknown error at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
} <br />
}<br />
if (nDevices == 0)<br />
Serial.println("No I2C devices found\n");<br />
else<br />
Serial.println("done\n");<br />
<br />
delay(5000); // wait 5 seconds for next scan<br />
}<br />
</pre><br />
<br />
====Bluetooth====<br />
It seems the Bluetooth functionality is the same as for the "Bluno" and thus the information at https://www.dfrobot.com/wiki/index.php/Bluno_SKU:DFR0267#Wireless_Programming_via_BLE applies.<br />
<br />
The AT commands seem to work, up to a point, but looks like it is a somewhat variant version of the firmware as AT+VERSION command doesn't work. Updating program seem not available for Linux.<br />
<br />
Booting the Vortex with the 'boot' button (near UL7, Vortex bottom-right-back RGB led.), two leds blink, and linux detects the Vortex over USB as:<br />
[230656.359259] usb 1-1.1: new full-speed USB device number 103 using xhci_hcd<br />
[230656.488502] usb 1-1.1: New USB device found, idVendor=2341, idProduct=0043<br />
[230656.488508] usb 1-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[230656.488511] usb 1-1.1: Product: DFRobot Boot CDC<br />
[230656.488513] usb 1-1.1: Manufacturer: DFRobot BLUno Boot<br />
[230656.490491] cdc_acm 1-1.1:1.0: ttyACM0: USB ACM device<br />
<br />
Similarly the little BLE dongle comes up as:<br />
[232626.931574] usb 1-1.3: new full-speed USB device number 108 using xhci_hcd<br />
[232627.254357] usb 1-1.3: New USB device found, idVendor=2341, idProduct=0043<br />
[232627.254362] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[232627.265758] cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device <br />
<br />
If Vortex pairs with the dongle, it effectiely gains an additional, wireless, serial link to a different port on the host. It's connected to the same Serial port on the internal Arduino. Make sure to adjust speed setting to what is used in the currently running sketch, the default 115200 won't work if Serial.begin(9600) is already called..<br />
<br />
<br />
Using AT commands it should be possible to set up the link connect to other Vortex robots too.<br />
<br />
Need to do some experiments to see if it is possible to connect to a generic Bluetooth 4.x dongle too.<br />
<br />
====Expansion====<br />
[[file:Vortex expansion holes drawing.svg|right|frame|hole arangement seen from top, vortex facing left or right]]<br />
There are 2 sets of 4 positions for screws/bolts at the top of the vortex, around the battery compartment, obviously to be used to put an expansion unit on top of Vortex, which could be linked to the i2c bus.<br />
<br />
=====Dimensions=====<br />
<br />
The bigger set of holes are 65 mm apart from left to right and 55 from front to back (center to center). These seem to be meant for 3 mm machine screws<br />
<br />
The smaller set of holes are 2.5" apart from left to right and 2.75" from front to back (center to center). These seem to be meant for small screws biting into the plastic.<br />
<br />
===== The 4 pin expansion port=====<br />
Q: Does Vortex support sensor module expansion?<br />
A: Vortex has a “Maker mode” where you can connect sensors via its IIC/TWI interface.<br />
<br />
It would seem the only logical place for this is the 4 pin connector in the back. Looking at the connector from the back of Vortex:<br />
____<br />
|1234|<br />
-__-<br />
<br />
These pins seem to be connected to the rainbow cable coming up from the main PCB as follows:<br />
<br />
{| class="wikitable"<br />
|+Vortex expansion port<br />
|-<br />
!Pin!!Function!!Color<br />
|-<br />
|1||SDA?||~15kOhm to Blue<br />
|-<br />
|2||SCL?||Blue<br />
|-<br />
|3||Gnd?||Black<br />
|-<br />
|4||Vcc?||Red<br />
|-<br />
|}<br />
<br />
It would make sense for Red to be 5V, Black to be Gnd, and the other two SCL and SDA of the i2c bus. The ~15kOhm between what I suspect to be SCL and SDA would then be two ~7.5kOhm to some common 5 V point that is buffered from the 'Red' cable.<br />
<br />
I did some further testing with a known good i2c device connected to the expansion port, but this just hung the i2c scanner or crashed Vortex depending on how I connected the port to the little display. Needs more work...<br />
<br />
Both pin 3 and 4 seem to be at +5V to the usb ground</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17772Luteijn/Thermostat2018-03-07T16:31:31Z<p>Luteijn: </p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* Remote controlled radiator(knobs) don't seem needed at first, just need to make sure the thermostatic knobs are set to match the desired max temperature, and trust the system to run the heater as long as at least one room needs heat. Eventually it would make things even better to have remote controlled radiators, so a room can be kept cooler than its max even if there is another room that needs to heat up. Should be a relatively simple operation to pop off the existing knobs and replace them by remote controlled ones that can be set to the desired temperature by the system, or just opened/closed more based on the heat demand from the room they are in.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== faceplate ====<br />
draft version [[file:Faceplate.svg]]<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|keepalive<br />
|state="alive"|"recovering"|"leaving"|"power"|<whatever><br />
|Periodic message to report (continued) presence, intention to leave, power issues (battery low etc.), and/or recovering from e.g. power outage, reset etc.<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Control node =====<br />
This will be located near a/the heater boiler/furnace (or airco, or ventilator or whatever) and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
* Desires and Engages directed to it<br />
<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit =====<br />
Basically just produces current sensor values and they occasional setting/friendly name message. Might consume settings to control its name and reporting frequency.<br />
<br />
===== Display unit =====<br />
Consumes:<br />
* Data produced by its associated sensor(s), which it displays.<br />
* Input from its associated manual input unit(s) <br />
* Settings to control what is associated with it etc.<br />
Produces:<br />
* Keepalives<br />
<br />
==== Power from the heater? ====<br />
As the heater on/of switch provides around 24V, maybe the controller can be run from that. Of course, if you complete the circuit with a short, the voltage is pulled down to 0, but maybe it is enough to use a pulldown, forming a voltage divider with whatever series R is used as a kind of pull up, but still enough for the heater to consider it a request for heat. <br />
<br />
Did some quick measurements to get an idea of what might be possible. At the Thermostat location in the living room, there is 22V available. When shorted with a 22kΩ resistor, this drops to 16.5V. So it seems that there is indeed some pull-up upstream.<br />
<br />
Assuming my multimeter is more or less perfect and no current of note runs when measuring voltage, and the cable used is also good, with no resistance of note, we have this voltage divider:<br />
<br />
0V---o---[22kΩ]---o---16.5V----[R]----22V<br />
<br />
So, 16.5V drops over 22kΩ, which means a current of (16.5/22)mA = 0.75mA flows (Ohm's law, I=U/R;R=U/I;U=RI). The same current flows through R, and 5.5V drops over R, so R is (5.5/0.75)kΩ = 7 1/3 kΩ<br />
<br />
If we drop the entire 22V over R alone, the expected current would be about 3mA, so this is probably the maximum we should try to draw. I checked, and indeed when short-circuiting with a multimeter, 3mA is exactly what's on the display.<br />
Anyway, 3mA at 22V is only 66mW, (22mA at 3V) so not a lot of power to work with, but might be enough for a small microprocessor. 'Real' thermostats use a kind of supercap to buffer energy.<br />
<br />
Next is to figure out what actually makes the heater switch on? If it is based on the voltage being drawn to (almost) 0, then bad luck. I suppose it is more likely what is being measured is current flowing through the thermostat, so any reasonable drop over R is enough. This kind of switch also has to work with less than perfect switches, after all. This might still mean bad luck, as it means we can't really pull much power from here, before triggering the heater...<br />
<br />
Next experiment would be putting in a variable resistor and see at what value of U/I the heater is triggered, but for now it seems a bit hopeless to use this idea.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=File:Faceplate.svg&diff=17771File:Faceplate.svg2018-03-07T15:44:19Z<p>Luteijn: Basic faceplate for putting a small screen in a candy-box</p>
<hr />
<div>Basic faceplate for putting a small screen in a candy-box</div>Luteijnhttps://revspace.nl/index.php?title=OpenDag2018&diff=17741OpenDag20182018-03-05T20:46:51Z<p>Luteijn: </p>
<hr />
<div>{{Event<br />
|Name=OpenDag2018<br />
|DateStart=31 March 2018<br />
|DateEnd=31 March 2018<br />
|InfoLocation=RevSpace<br />
|InfoOpen=11:00<br />
}}<br />
{{NewsItem<br />
|Name=Jaarlijkse open Hackerspaces dag (2018)<br />
|Date=2018-03-31<br />
|Info=''Op zaterdag 31 maart kan iedereen weer vrij binnenlopen in een hackerspace. Tijdens deze open dag is het mogelijk om te zien wat bijvoorbeeld hacken inhoudt en te zien wat er allemaal kan in een hackerspace.''<br />
}}<br />
<br />
== Bezoek RevSpace ==<br />
<br />
Zie [[:file:Opendag2018-achterkant.svg| achterop de flyer]], maar in het kort komt het erop neer dat ook RevSpace, de hackerspace voor Den Haag en omgeving op #1 maart een open dag houdt. Als je altijd al eens een kijkje had willen nemen, of pas net van het bestaan van Hackerspaces in het algemeen en/of RevSpace in het bijzonder hebt gehoord, kom dan vooral langs. We doen ons best om alles nog laagdrempeliger dan de reguliere openingen op de dinsdagen te maken. We zijn die dag vanaf 11.00 helemaal open. Adres etc. staan, behalve op de [[:file:Opendag2018-achterkant.svg|flyer]] ook wat uitgebreider hier op de site (https://revspace.nl/Bezoeken). <br />
<br />
Hopelijk tot ziens op onze Open Dag. Komt die niet uit, maar is wel je interesse gewekt, dan kun je ook op een ander moment langskomen. Zoals op de [[Main_Page|voorpagina]] staat zijn we op de dinsdagen om 18.00 open, maar ook vaak op andere dagen.<br />
<br />
<br />
<br />
== Organisatie ==<br />
<br />
Landelijk ingegeven via: https://hackerspaces.nl/open-dag/<br />
<br />
RevSpace doet uiteraard ook mee.<br />
<br />
=== Aanwezigen ===<br />
<br />
Om zeker te weten dat we met genoeg mensen zijn, een lijstje van revspacers die hebben toegezegd te komen om bezoekers te ontvangen:<br />
<br />
{|class=wikitable<br />
! Naam !! Van-tot !! Opmerkingen<br />
|-<br />
| Sebastius || 11.00 - laat || Rondleiden, lasercutten, algemeen knutselen.<br />
|-<br />
| Benadski || 11.00 - laat || Soldeerworkshop (kitjes).<br />
|-<br />
| Jij? || ? || <br />
|}<br />
<br />
=== Promotie ===<br />
* <s>SuperLisa maakt de flyer, A5 dubbelzijdig, full colour. ETA: mid/eind februari. Andere ontwerpers ook welkom!</s><br />
** Voorkant van flyer: [[:File:Opendag2018-voorkant.png]], [[:File:Opendag2018-voorkant.svg]], [[:File:Opendag2018-voorkant.pdf]]<br />
** Achterkant van flyer: [[:File:Opendag2018-achterkant.png]], [[:File:Opendag2018-achterkant.svg]], [[:File:Opendag2018-achterkant.pdf]]<br />
* Wie contacteert welke krant? (benadski heeft vorig jaar het AD, het Krantje en de Posthoorn benaderd)<br />
* Jij doet??<br />
<br />
=== Activiteiten ===<br />
* Rondleidingen langs onze werkplaatsen, apparatuur en projecten.<br />
* Op verzoek: demonstratie van apparatuur zoals lasersnijder, 3D-printer, foliesnijder, oscilloscoop, etc...<br />
* Soldeerworkshop: Benadski ontwerpt iets wat leuk geluid maakt met de 4093. De stichting financiert 30 kitjes.</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17736Luteijn/Thermostat2018-03-05T11:04:41Z<p>Luteijn: /* Power from the heater? */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* Remote controlled radiator(knobs) don't seem needed at first, just need to make sure the thermostatic knobs are set to match the desired max temperature, and trust the system to run the heater as long as at least one room needs heat. Eventually it would make things even better to have remote controlled radiators, so a room can be kept cooler than its max even if there is another room that needs to heat up. Should be a relatively simple operation to pop off the existing knobs and replace them by remote controlled ones that can be set to the desired temperature by the system, or just opened/closed more based on the heat demand from the room they are in.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|keepalive<br />
|state="alive"|"recovering"|"leaving"|"power"|<whatever><br />
|Periodic message to report (continued) presence, intention to leave, power issues (battery low etc.), and/or recovering from e.g. power outage, reset etc.<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Control node =====<br />
This will be located near a/the heater boiler/furnace (or airco, or ventilator or whatever) and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
* Desires and Engages directed to it<br />
<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit =====<br />
Basically just produces current sensor values and they occasional setting/friendly name message. Might consume settings to control its name and reporting frequency.<br />
<br />
===== Display unit =====<br />
Consumes:<br />
* Data produced by its associated sensor(s), which it displays.<br />
* Input from its associated manual input unit(s) <br />
* Settings to control what is associated with it etc.<br />
Produces:<br />
* Keepalives<br />
<br />
==== Power from the heater? ====<br />
As the heater on/of switch provides around 24V, maybe the controller can be run from that. Of course, if you complete the circuit with a short, the voltage is pulled down to 0, but maybe it is enough to use a pulldown, forming a voltage divider with whatever series R is used as a kind of pull up, but still enough for the heater to consider it a request for heat. <br />
<br />
Did some quick measurements to get an idea of what might be possible. At the Thermostat location in the living room, there is 22V available. When shorted with a 22kΩ resistor, this drops to 16.5V. So it seems that there is indeed some pull-up upstream.<br />
<br />
Assuming my multimeter is more or less perfect and no current of note runs when measuring voltage, and the cable used is also good, with no resistance of note, we have this voltage divider:<br />
<br />
0V---o---[22kΩ]---o---16.5V----[R]----22V<br />
<br />
So, 16.5V drops over 22kΩ, which means a current of (16.5/22)mA = 0.75mA flows (Ohm's law, I=U/R;R=U/I;U=RI). The same current flows through R, and 5.5V drops over R, so R is (5.5/0.75)kΩ = 7 1/3 kΩ<br />
<br />
If we drop the entire 22V over R alone, the expected current would be about 3mA, so this is probably the maximum we should try to draw. I checked, and indeed when short-circuiting with a multimeter, 3mA is exactly what's on the display.<br />
Anyway, 3mA at 22V is only 66mW, (22mA at 3V) so not a lot of power to work with, but might be enough for a small microprocessor. 'Real' thermostats use a kind of supercap to buffer energy.<br />
<br />
Next is to figure out what actually makes the heater switch on? If it is based on the voltage being drawn to (almost) 0, then bad luck. I suppose it is more likely what is being measured is current flowing through the thermostat, so any reasonable drop over R is enough. This kind of switch also has to work with less than perfect switches, after all. This might still mean bad luck, as it means we can't really pull much power from here, before triggering the heater...<br />
<br />
Next experiment would be putting in a variable resistor and see at what value of U/I the heater is triggered, but for now it seems a bit hopeless to use this idea.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17735Luteijn/Thermostat2018-03-05T10:35:57Z<p>Luteijn: /* comments welcome */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* Remote controlled radiator(knobs) don't seem needed at first, just need to make sure the thermostatic knobs are set to match the desired max temperature, and trust the system to run the heater as long as at least one room needs heat. Eventually it would make things even better to have remote controlled radiators, so a room can be kept cooler than its max even if there is another room that needs to heat up. Should be a relatively simple operation to pop off the existing knobs and replace them by remote controlled ones that can be set to the desired temperature by the system, or just opened/closed more based on the heat demand from the room they are in.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|keepalive<br />
|state="alive"|"recovering"|"leaving"|"power"|<whatever><br />
|Periodic message to report (continued) presence, intention to leave, power issues (battery low etc.), and/or recovering from e.g. power outage, reset etc.<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Control node =====<br />
This will be located near a/the heater boiler/furnace (or airco, or ventilator or whatever) and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
* Desires and Engages directed to it<br />
<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit =====<br />
Basically just produces current sensor values and they occasional setting/friendly name message. Might consume settings to control its name and reporting frequency.<br />
<br />
===== Display unit =====<br />
Consumes:<br />
* Data produced by its associated sensor(s), which it displays.<br />
* Input from its associated manual input unit(s) <br />
* Settings to control what is associated with it etc.<br />
Produces:<br />
* Keepalives<br />
<br />
==== Power from the heater? ====<br />
As the heater on/of switch provides around 24V, maybe the controller can be run from that. Of course, if you complete the circuit with a short, the voltage is pulled down to 0, but maybe it is enough to use a pulldown, forming a voltage divider with whatever series R is used as a kind of pull up, but still enough for the heater to consider it a request for heat. <br />
<br />
Did some quick measurements to get an idea of what might be possible. At the Thermostat location in the living room, there is 22V available. When shorted with a 22kΩ resistor, this drops to 16.5V. So it seems that there is indeed some pull-up upstream.<br />
<br />
Assuming my multimeter is more or less perfect and no current of note runs when measuring voltage, and the cable used is also good, with no resistance of note, we have this voltage divider:<br />
<br />
0V---o---[22kΩ]---o---16.5V----[R]----22V<br />
<br />
So, 16.5V drops over 22kΩ, which means a current of (16.5/22)mA = 0.75mA flows (Ohm's law, I=U/R;R=U/I;U=RI). The same current flows through R, and 5.5V drops over R, so R is (5.5/0.75)kΩ = 7 1/3 kΩ<br />
<br />
If we drop the entire 22V over R alone, the expected current would be about 3mA, so this is probably the maximum we should try to draw. Need to make a control-measurement, but multimeter is missing its fuse at the moment.<br />
Anyway, 3mA at 22V is only 66mW, (22mA at 3V) so not a lot of power to work with, but might be enough for a small microprocessor. 'Real' thermostats use a kind of supercap to buffer energy.<br />
<br />
<br />
Next is to figure out what actually makes the heater switch on? If it is based on the voltage being drawn to (almost) 0, then bad luck. I suppose it is more likely what is being measured is current flowing through the thermostat, so any reasonable drop over R is enough. This kind of switch also has to work with less than perfect switches, after all. This might still mean bad luck, as it means we can't really pull much power from here, before triggering the heater...<br />
<br />
Next experiment would be putting in a variable resistor and see at what value of U/I the heater is triggered, but for now it seems a bit hopeless to use this idea.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17734Luteijn/Thermostat2018-03-05T07:58:38Z<p>Luteijn: /* Basic idea */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* Remote controlled radiator(knobs) don't seem needed at first, just need to make sure the thermostatic knobs are set to match the desired max temperature, and trust the system to run the heater as long as at least one room needs heat. Eventually it would make things even better to have remote controlled radiators, so a room can be kept cooler than its max even if there is another room that needs to heat up. Should be a relatively simple operation to pop off the existing knobs and replace them by remote controlled ones that can be set to the desired temperature by the system, or just opened/closed more based on the heat demand from the room they are in.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|keepalive<br />
|state="alive"|"recovering"|"leaving"|"power"|<whatever><br />
|Periodic message to report (continued) presence, intention to leave, power issues (battery low etc.), and/or recovering from e.g. power outage, reset etc.<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Control node =====<br />
This will be located near a/the heater boiler/furnace (or airco, or ventilator or whatever) and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
* Desires and Engages directed to it<br />
<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit =====<br />
Basically just produces current sensor values and they occasional setting/friendly name message. Might consume settings to control its name and reporting frequency.<br />
<br />
===== Display unit =====<br />
Consumes:<br />
* Data produced by its associated sensor(s), which it displays.<br />
* Input from its associated manual input unit(s) <br />
* Settings to control what is associated with it etc.<br />
Produces:<br />
* Keepalives<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17726Luteijn/Thermostat2018-03-03T17:40:28Z<p>Luteijn: /* = Sensor unit */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|keepalive<br />
|state="alive"|"recovering"|"leaving"|"power"|<whatever><br />
|Periodic message to report (continued) presence, intention to leave, power issues (battery low etc.), and/or recovering from e.g. power outage, reset etc.<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Control node =====<br />
This will be located near a/the heater boiler/furnace (or airco, or ventilator or whatever) and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
* Desires and Engages directed to it<br />
<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit =====<br />
Basically just produces current sensor values and they occasional setting/friendly name message. Might consume settings to control its name and reporting frequency.<br />
<br />
===== Display unit =====<br />
Consumes:<br />
* Data produced by its associated sensor(s), which it displays.<br />
* Input from its associated manual input unit(s) <br />
* Settings to control what is associated with it etc.<br />
Produces:<br />
* Keepalives<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17725Luteijn/Thermostat2018-03-03T17:40:13Z<p>Luteijn: /* Ideas for events to generate and/or react to */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|keepalive<br />
|state="alive"|"recovering"|"leaving"|"power"|<whatever><br />
|Periodic message to report (continued) presence, intention to leave, power issues (battery low etc.), and/or recovering from e.g. power outage, reset etc.<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Control node =====<br />
This will be located near a/the heater boiler/furnace (or airco, or ventilator or whatever) and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
* Desires and Engages directed to it<br />
<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit ====<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17724Luteijn/Thermostat2018-03-03T17:31:09Z<p>Luteijn: /* Control node */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|K<br />
|<br />
|<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Control node =====<br />
This will be located near a/the heater boiler/furnace (or airco, or ventilator or whatever) and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
* Desires and Engages directed to it<br />
<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit ====<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17723Luteijn/Thermostat2018-03-03T17:28:53Z<p>Luteijn: </p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|barometer<br />
|<barometric pressure><br />
|Current Barometric pressure<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|CO2 level measured<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|gravity<br />
|<field strength><br />
|Now we're just making things up :)<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|I<br />
|<current><br />
|Current flowing<br />
|-<br />
|J<br />
|<br />
|<br />
|-<br />
|K<br />
|<br />
|<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|query:<what><br />
|[target=<target>]<br />
|request information to be re-published<br />
|-<br />
|relay:<what><br />
|<value><br />
|Reports relay state,0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|V<br />
|<voltage><br />
|Voltage level<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|xrays<br />
|<level><br />
|Roentgen radiation level<br />
|-<br />
|Y<br />
|<br />
|<br />
|-<br />
|Z<br />
|<br />
|<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Heater node =====<br />
This will be located near a/the heater boiler/furnace and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
*<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit ====<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17722Luteijn/Thermostat2018-03-03T13:01:57Z<p>Luteijn: /* Ideas for events to generate and/or react to */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
Topic of the MQTT info should be of the form /.../<node-ID>/[sub id/]<topic>, with the message payload as per the table below. Node ID's should be a short type indicator(2-4 characters), a dash, and a three digit number). Friendly names can be given separately. Sub id's should be numbers from 1-whatever, with 0 being the implied sub id for units with only one of the type of sensors c.q. the value representing the 'average' of all subsensors of a type. The topic indicates the type of sensor or desire this report is about.<br />
<br />
Examples:<br />
/RLL255/sensors/CO2-001/co2 739 PPM <br />
/RLL255/sensors/CO2-001/0/co2 739 PPM <br />
/RLL255/sensors/room-001/1/temperature 17.1 °C<br />
/RLL255/sensors/room-001/2/temperature 17.2 °C<br />
/RLL255/sensors/room-001/alert message="Report to the Mess:Dinner!",target=room-011<br />
<br />
{| class="wikitable"<br />
!Topic<br />
!Message payload<br />
!Comments<br />
|-<br />
|alert<br />
|message="<message>"[,target="<target-id>"[,...]]<br />
|Target is by default all nodes, but one or more specific targets can be specified by their ID<br />
|-<br />
|B<br />
|<br />
|<br />
|-<br />
|co2<br />
|<CO2 PPM><br />
|<br />
|-<br />
|desire:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values, e.g. 1 to indicate there is an ongoing need that is currently being satisfied (to avoid pingponging between a heater on/off). <what> could be e.g. heat,no_heat or ventilator. This indicates a locally computed desire based on settings and sensor input. Nodes that can fulfill these desires should weigh their decisions and decide to pitch in or not.<br />
|-<br />
|engage:<what><br />
|<value>,<target-id><br />
|This is a more or less explicit directive for a node to engage its <what> (typically heater relay). To be used by more or less 'smart' units to force relatively 'dumb' units to forget about making their own decisions and just defer to whomever they trust most. Value=0 to cancel the directive, and everything else as a priority to act as tie breaker between conflicting orders between equally trusted nodes. NORMALLY a node with a relay would be sufficiently smart not to need this and just react to desire_<what> instead. <br />
|-<br />
|friendly_name<br />
|<name><br />
|The friendly/display name for this node, e.g. a room name describing the room it is in<br />
|-<br />
|G<br />
|<br />
|<br />
|-<br />
|humidity<br />
|<RH><br />
|current relative humidity<br />
|-<br />
|light<br />
|<light level><br />
|current light level (in whatever unit makes sense)<br />
|-<br />
|manual:<what><br />
|<value><br />
|probably just 0 for 'False', -1 for 'True', but might have meaningful other values. <what> could be e.g. heat or ventilator.<br />
This indicates a locally made manual request that should be given a lot of weight in any further decisions<br />
|-<br />
|noise<br />
|<noise level><br />
|current noise level (in whatever unit makes sense)<br />
|-<br />
|occupants<br />
|<number of occupants><br />
|Either a guess at the number of occupants, based on user entry or other inputs, or simply -1 for undetermined amount >0.<br />
|-<br />
|pir<br />
|<movement sensed><br />
|probably just 0 for 'False', -1 for 'True'<br />
|-<br />
|relay:<what><br />
|<value><br />
|0 for at rest, -1 to indicate engaged. <what> is typically heat or maybe ventilation<br />
|-<br />
|setting:<key><br />
|value="value"<br />
|settings for the unit, like schedule, target temperature, humidity alert levels etc.<br />
|-<br />
|temperature<br />
|<temperature><br />
|Current ambient temperature<br />
|-<br />
|unixtimestamp<br />
|<timestamp in seconds since epoch><br />
|Current time at reporting unit. Other units could sync to this if they trust this time more than their own. Units without a properly set clock should not usually report their time to avoid 'dumb' units to sync to unreliable time. Units should generally only sync to one, trusted, source.<br />
|-<br />
|water_level<br />
|<level><br />
|Level of water in crawlspace, sink-well, cistern etc.<br />
|-<br />
|}<br />
<br />
===== Room node =====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input unit, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
<br />
Produces:<br />
* Sensor readings (e.g. Temperature, CO2 level, Rel. Humidity, Occupants, Ambient light, <br />
* Settings (e.g. current target temperature, current switching scheme/schedule)<br />
* Local User input (e.g. override to just request heat with priority, or to copy settings from a default or other node)<br />
Consumes: <br />
* Date/Time updates (to keep clock in sync)<br />
* Its own published measurements setting etc. to update local status<br />
* Published measurements from other nodes to display when requested by user<br />
* 'PA' messages ("All Hands, report to the galley"; "Doorbell"; "Incoming phonecall"; "DEFCON change")<br />
<br />
===== Heater node =====<br />
This will be located near a/the heater boiler/furnace and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
Produces:<br />
* Anything a normal Room node would<br />
* Relay state<br />
Consumes:<br />
* Anything a normal Room node would<br />
*<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not.<br />
<br />
===== Sensor unit ====<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17720Luteijn/Thermostat2018-03-03T11:23:48Z<p>Luteijn: /* comments welcome */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== Ideas for events to generate and/or react to ====<br />
There will be several slightly different types of units that might differ in the types of events that are useful to them.<br />
Most nodes would not keep most of their state locally, but rely on values published to MQTT (most likely by themselves), so they can quickly recover from resets and the like, just based on their 'name' and questioning persistent MQTT info.<br />
<br />
<br />
===== Room node=====<br />
A room node most likely will combine one or more environment sensor(s) with a status display and a manual input, sharing a power source, microcontroller and connectivity, as most of the time you'd want all three of these functions in each room anyway, and duplicating most of these into separate units probably is not very efficient.<br />
===== Heater node =====<br />
This will be located near a/the heater boiler/furnace and should be the only unit actually directly talking to it. It might include more or less 'intelligence' to make a decision based on possibly conflicting demands from different other nodes.<br />
===== Computer node =====<br />
This would be running on a 'server' type of computer (not a microcontroller), and use information from different sources to come to a compound decision on turning on the heat or not. <br />
<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17719Luteijn/Thermostat2018-03-03T11:06:08Z<p>Luteijn: /* Mock up of display */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
To the left a crude version of the large font showing 17. Arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17717Luteijn/Thermostat2018-03-02T18:51:52Z<p>Luteijn: /* comments welcome */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
to the left a crude version of the large font showing 17. arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2<br />
<br />
https://www.verwarmenperkamer.nl/hoe-werkt/thermostaatkraan-handkraan</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17716Luteijn/Thermostat2018-03-02T18:51:16Z<p>Luteijn: /* comments welcome */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
to the left a crude version of the large font showing 17. arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)<br />
<br />
<br />
https://www.amazon.de/komforthaus-Heizkörperthermostat-Version-stabiler-Metallmutter/dp/B01A5R1I8K/ref=sr_1_2?ie=UTF8&qid=1519944121&sr=8-2</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17715Luteijn/Thermostat2018-03-02T14:11:01Z<p>Luteijn: /* Basic idea */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (a relay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
to the left a crude version of the large font showing 17. arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17714Luteijn/Thermostat2018-03-02T13:36:09Z<p>Luteijn: /* Mock up of display */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (arelay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
to the left a crude version of the large font showing 17. arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
|<br />
| +<br />
|-<br />
|_ <br />
|█ <br />
|_ <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|<br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17712Luteijn/Thermostat2018-03-01T22:23:43Z<p>Luteijn: /* comments welcome */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (arelay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
to the left a crude version of the large font showing 17. arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
| +<br />
|-<br />
| <br />
|█ <br />
| <br />
| <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== comments welcome ====<br />
23:21 <@Juerd> luteijn: https://github.com/Juerd/eq3-max voor inspiratie :)</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17711Luteijn/Thermostat2018-03-01T13:34:02Z<p>Luteijn: /* LCD Layout */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (arelay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running. Pressing a button could cycle the display to show settings like room name, overall system status, and/or information of other things going on in the house. After all, if you have a small screen able to talk/listen to MQTT in each room, might as well use it to display events coming in. <br />
<br />
=====Mock up of display=====<br />
to the left a crude version of the large font showing 17. arrow indicates target temperature,humidity (if applicable is also shown). The 🔥 flame to show boiler is on (reported from boiler controller), and a '+' is another status indicator e.g. showing this node wants heat.<br />
<br />
{| class="wikitable"<br />
|▀ <br />
|█ <br />
| <br />
| <br />
|▀ <br />
|▀ <br />
|█ <br />
|o<br />
|→ <br />
|2 <br />
|0 <br />
|° <br />
|3 <br />
|2<br />
|% <br />
| +<br />
|-<br />
| <br />
|█ <br />
| <br />
| <br />
| <br />
|█ <br />
|<br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7<br />
|: <br />
|4 <br />
|2 <br />
|🔥 <br />
|}<br />
<br />
A rotary controller would be fun, but 3-4 buttons (next item,select,prev item,cancel) would also work as a control panel for local control/cycling through other displays.<br />
<br />
==== comments welcome ====</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17710Luteijn/Thermostat2018-03-01T11:57:52Z<p>Luteijn: /* Project "Thermostat": */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (arelay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells, or alternatively seen, 6x9 pixels with one 'dead' row and column per cell. Or, a screen of 95x17 pixels with a few lines in it. Unfortunately there are only 8 programmable characters so it's not possible to fake a completely bitmapped display, but an 8 cell area could be done, and there are libraries for doing a 'big' font using reusable tiles.<br />
<br />
A Thermostat display should probably by default show current room temperature in the big font, and the current target temperature in a smaller one, as well as a small clock and a symbol indicating if this thermostat is requesting heat, and a flame on to indicate if the boiler is currently running.<br />
<br />
Mock up of display<br />
<br />
{| class="wikitable"<br />
|^ <br />
|/ <br />
| <br />
| <br />
|^ <br />
|^ <br />
|/ <br />
|O<br />
| <br />
|1 <br />
|3 <br />
|: <br />
|3 <br />
|7 <br />
| <br />
| <br />
|-<br />
| <br />
|# <br />
| <br />
| <br />
| <br />
|\ <br />
| <br />
|=> <br />
|2 <br />
|0 <br />
|° <br />
| <br />
|W <br />
| <br />
| <br />
| <br />
|}<br />
==== comments welcome ====</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17709Luteijn/Thermostat2018-03-01T11:23:42Z<p>Luteijn: /* Project "Thermostat": */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Thermostat<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (arelay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
<br />
==== LCD Layout ====<br />
16x2 cells, each having 5x8 pixels, with a 1 pixel gap between cells. This template should help doing layouts for UI's using this display.<br />
<br />
<br />
<br />
<br />
==== comments welcome ====</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17708Luteijn/Thermostat2018-03-01T09:57:36Z<p>Luteijn: /* Project "Thermostat": */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Vortex<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=ChronothermIV.png<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the living room. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (arelay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
==== comments welcome ====</div>Luteijnhttps://revspace.nl/index.php?title=File:ChronothermIV.png&diff=17707File:ChronothermIV.png2018-03-01T09:56:22Z<p>Luteijn: Stock picture of ChronoTherm IV Thermostat</p>
<hr />
<div>Stock picture of ChronoTherm IV Thermostat</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17706Luteijn/Thermostat2018-03-01T09:53:55Z<p>Luteijn: /* Background */</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Vortex<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the livingroom. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (arelay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
==== comments welcome ====</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Thermostat&diff=17705Luteijn/Thermostat2018-03-01T09:53:34Z<p>Luteijn: Created page with "===Project "Thermostat":=== {{Project |Name=Luteijn/Vortex |Status=In progress |Contact=Luteijn |Picture= }} ==== Background ==== My house has a thermostat in the livingroom..."</p>
<hr />
<div>===Project "Thermostat":===<br />
{{Project<br />
|Name=Luteijn/Vortex<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=<br />
}}<br />
<br />
==== Background ====<br />
My house has a thermostat in the livingroom. It controls the central heating bij switching a boiler on and off. Although it is a relatively advanced thermostat (Honeywell Chronotherm IV), that even has some provisions for remote controlling/overriding, and extra temperature sensors, I just have the basic connector-plate that is missing the physical connections for this. And I'm not going to bother hacking them in, as I'm not satisfied with the current set-up for several reasons.<br />
* My version of the Thermostat is the basic on-off non-modulating version, as at the time I bought it, the boiler didn't support Open Therm anyway, and Open Therm is only 'open' for certain values of 'open'. Boiler is upgraded and now has Open Therm capability, also quite a bit of the Open Therm stuff is now opened up by reverse engineering etc.<br />
* I don't care that the temperature in the living room is 'adequate' if I'm in the study where it is not adequate. If it is too hot in the study, I could turn down the radiator there, but if it is too cold in the study and the living room thermostat has shut down the boiler, it won't get any warmer. <br />
* Although there is some intelligence/self-learning included in the thermostat, so it starts heating in time to have the house warm at the scheduled time, it doesn't handle rapid weather changes too well, and the scheduling is a bit limited. It would be nice if it could read the weather forecast and be a bit more aware of presence and adjust its program on that.<br />
* I don't want a 'cloud-based' thermostat, but don't mind a home computer making the decisions.<br />
<br />
==== Some related info ====<br />
Installers manual for a similar Chronotherm IV explaining how to get to the installer's menu (TL;DR: press all three buttons [i] [^] and [v] at the same time for a few moments), and what to do when you get there to activated more features: https://customer.honeywell.com/resources/techlit/TechLitDocuments/69-0000s/69-1510.pdf <br />
<br />
Bigger characters to make a large 'current temperature' display: http://www.gregington.com/2013/10/displaying-large-text-on-lcd-displays.html<br />
<br />
==== Basic idea ====<br />
* Replace the thermostat by a relay/FET capable of handling the 24 V 'interface' to the Boiler. (arelay is enough for basic on-off 24V, FET probably needed if I want to talk Open Therm to it instead). Control this 'switch' with a microcontroller that can take more input into account than the Chronotherm can/does.<br />
* Initially just have the Chronotherm proxied by the microcontroller via a direct wired connection, then built up from there.<br />
* An ESP would seem like a useful microcontroller, as it could get its input(s) wirelessly, and a nice step two would be to have one ESP control the boiler, and another connected to the Chronotherm, so it can be easily moved to whatever room I want it to monitor, and have them talk to each other, either directly or via MQTT.<br />
* Next, move the final decision making away from the Chronotherm, and make it just one of potentially many sensors providing suggestions. Final decision to turn on/off the Heater would be made on the microcontroller controlling the Boiler, but it might be 'strongly influenced' by a more intelligent program running on a homeserver.<br />
* Add more mini-thermostats/sensors throughout the house that can indicate the 'need for heat' based on current temperature, Rel. Humidity, user-input (e.g. a big 'I am fscking cold, activate boiler now - RED ALERT, all engines flanking speed, ahead warp-factor 9, damn the torpedoes, go to defcon 1, dive-dive-dive, women-and-children-first'-panicbutton) and basically OR all these to decide to ('strongly suggest' the microcontroller to) switch on the heater. These would/could/should have a little 'flame' display/led/indicator to show the current boiler state too, to reassure the user(s) heat is on its way/not being generated needlessly.<br />
* Graph the information collected by the sensors to try and find patterns to optimize the decision process and hopefully avoid user-intervention as much as possible.<br />
* etc.<br />
<br />
==== Work done so far ====<br />
* Not enough.<br />
* Made a wikipage<br />
* Looked into working of existing heating set-up.<br />
* Ordered some small ESP based boards and sensors from China.<br />
* Experimented with 16x2 LCD displays to be used as status displays. <br />
* Practiced sensor graphing from existing CO2/Temp/Hum meter (similar to Voltcraft C-60)<br />
* Looked into existing stand alone e-thermometers to be enhanced by an ESP or gutted for parts. <br />
* Got some experience controlling relay by microcontroller to open garage-door<br />
* Analogue sensor experience from garage-door open sensor. (As it only had an analogue pin left that can't be allowed to go completely low, it doubles as the active low RESET pin)<br />
* Saved up some old candy tins and toothpaste caps that might work nice as housing and knobs for rotary encoders<br />
<br />
Basically waiting for more parts to arrive and some free time to make the Chronotherm work wirelessly and experiment with placing it in different rooms.<br />
<br />
==== comments welcome ====</div>Luteijnhttps://revspace.nl/index.php?title=Keramiekoven&diff=17680Keramiekoven2018-02-26T10:05:31Z<p>Luteijn: /* ToDos */</p>
<hr />
<div>{{Project <br />
|Name=Keramiek oven<br />
|Status=Completed<br />
|Picture=IMG_20180203_143058.jpg<br />
|Contact=User:Gert Kremer<br />
|Contact1=User:Gori<br />
}}<br />
<br />
<br />
__TOC__<br />
<br />
== Technische specificaties ==<br />
* Binnenmaat: 39x39x46cm<br />
* Maximaal temperatuur: 1200 Celsius<br />
* Maximaal vermogen: ~10.5 kW<br />
* Gewicht: niet gemeten, tegen 100 kg<br />
* Buitenmaten:<br />
* Model: Vingerling Gouda, Type V 240 automatic<br />
* User manual:<br />
** 10:19 <@Pwuts> gori: Keramikos heeft een PDF handleiding van de V240 gestuurd!!1!!11 \o\ /o/ \o/<br />
** Model V 230: [[File:Vingerling_V230_oven.pdf]] - Voor zover ik het kan overzien, dit is de kleinere, 1 fase broertje van V240. Allerlei zaken met hittestaafjes e.d. zijn niet van toepassing.<br />
** Controle unit V256: [[File:Vingerling_V256_Aansturing.pdf]]<br />
<br />
<br />
<gallery><br />
File:IMG_20180203_142957.jpg<br />
File:IMG_20180203_143543.jpg<br />
File:IMG_20180203_143058.jpg<br />
File:IMG_20180203_143046.jpg<br />
File:IMG_20180203_143034.jpg<br />
File:IMG_20180203_142957.jpg<br />
</gallery><br />
<br />
== Regels gebruik ==<br />
* Oven mag niet binnen in de space gebruikt worden.<br />
* In de sparkshack aansluiten op het 3-fasen stopcontact, dat gemeterd is. Noteer begin en eindstand.<br />
* Je moet de gebruikte kWh afrekenen. Op dit moment geldt 0.21 Euro / kWh. De totale bedrag via revbank "give keramiekoven <bedrag>" <br />
* Als de oven helemaal afgekoeld is, terugbrengen naar de werkplaats, links onder in de stellage. Zorg dat de oven helemaal naar achteren zit, en niet over de gele lijn uitsteekt.<br />
<br />
== Oven gebruiken ==<br />
* Mail AUB naar de participantsonly@revspace.nl als je heel lang gaat stoken, gezien dat de sparkschack niet echt te gebruiken is in de tussentijd. <br />
* Oven langzaam uit zijn opslagplaats in de werkplaats rijden, let op dat je niet aan de stellingkast blijft hangen, er zitten uitstekende schroefjes aan de zijkanten<br />
* Rijd de oven naar de sparkshack, dit gaat makkelijker met twee personen.<br />
* Installeer de aansturing door eerst de stekker aan de achterkant te bevestigen, en vervolgens het thermokoppel in te steken. Je hebt keuze uit voor, achter en zijkant.<br />
* Zet de aansturingsunit NIET op de bovenkant, want daar wordt het heet.<br />
* Zorg dat je object niet direct op de bodem van de oven staat, en niet het verwarmingselement aanraakt/kortsluit<br />
* Stekker in het stopcontact, noteer de meterstand, zet de grote schakelaar aan.<br />
* Stel de gewenste temperatuur, stookcurve en timer in, leg de keramische platen over de bovenopening, en veel plezier<br />
* Noteer hieronder je ervaringen over wat, hoe veel, hoe lang en op welke stookcurves je uitkomt, zodat we ervaring opbouwen.<br />
<br />
== ToDos ==<br />
* Oven steunen en aantal platen regelen, zodat we meerdere dingen tegelijkertijd kunnen stoken. <br />
* Een plug gieten van gips/chamotte cement, zodat het afgesloten en geopend kan worden. <br />
* Stuk keramiekwol regelen, en bevestigen aan de onderkant, om die nog beter te isoleren. <br />
* Aansturing digitaal maken, zodat we willekeurige stook curves er in kunnen zetten<br />
* Staal van de bovenkant repareren of vervangen, buitenkant schuren en in hoog-temperatuur verf zetten, liefst knal rood ;)<br />
* Via Pwuts, mail van Broere uit Gouda: "Wij hebben in het verleden ovens gemaakt voor Vingerling. De productie van deze ovens is in 1999 gestaakt. Tot 2008 hebben wij nog service verleend aan de Vingerling ovens. In dat jaar hebben wij het verlenen van service overgedragen aan de firma Keramikos in Haarlem. Website: http://www.keramikos.nl/ . U kunt zich tot dat bedrijf wenden voor reparaties en onderhoud." - De Keramikos website ziet er niet 'hacker-onvriendelijk' uit, dus wellicht dat daar mogelijkheden zijn om informatie los te krijgen? --> Heeft een PDF handleiding opgeleverd die nog wel even hier geupload moet worden.<br />
<br />
== Ervaringen ==<br />
Er zijn meerdere soort dingen die we met de oven kunnen doen<br />
<br />
=== Keramiek bakken ===<br />
* ...<br />
<br />
=== Glas fusen ===<br />
* ...<br />
<br />
=== Metalen smelten ===<br />
* ...<br />
<br />
=== Glas / metaal annealen ===<br />
* ...<br />
<br />
=== ??? ===<br />
* ...</div>Luteijnhttps://revspace.nl/index.php?title=Keramiekoven&diff=17679Keramiekoven2018-02-26T09:57:42Z<p>Luteijn: /* Technische specificaties */</p>
<hr />
<div>{{Project <br />
|Name=Keramiek oven<br />
|Status=Completed<br />
|Picture=IMG_20180203_143058.jpg<br />
|Contact=User:Gert Kremer<br />
|Contact1=User:Gori<br />
}}<br />
<br />
<br />
__TOC__<br />
<br />
== Technische specificaties ==<br />
* Binnenmaat: 39x39x46cm<br />
* Maximaal temperatuur: 1200 Celsius<br />
* Maximaal vermogen: ~10.5 kW<br />
* Gewicht: niet gemeten, tegen 100 kg<br />
* Buitenmaten:<br />
* Model: Vingerling Gouda, Type V 240 automatic<br />
* User manual:<br />
** 10:19 <@Pwuts> gori: Keramikos heeft een PDF handleiding van de V240 gestuurd!!1!!11 \o\ /o/ \o/<br />
** Model V 230: [[File:Vingerling_V230_oven.pdf]] - Voor zover ik het kan overzien, dit is de kleinere, 1 fase broertje van V240. Allerlei zaken met hittestaafjes e.d. zijn niet van toepassing.<br />
** Controle unit V256: [[File:Vingerling_V256_Aansturing.pdf]]<br />
<br />
<br />
<gallery><br />
File:IMG_20180203_142957.jpg<br />
File:IMG_20180203_143543.jpg<br />
File:IMG_20180203_143058.jpg<br />
File:IMG_20180203_143046.jpg<br />
File:IMG_20180203_143034.jpg<br />
File:IMG_20180203_142957.jpg<br />
</gallery><br />
<br />
== Regels gebruik ==<br />
* Oven mag niet binnen in de space gebruikt worden.<br />
* In de sparkshack aansluiten op het 3-fasen stopcontact, dat gemeterd is. Noteer begin en eindstand.<br />
* Je moet de gebruikte kWh afrekenen. Op dit moment geldt 0.21 Euro / kWh. De totale bedrag via revbank "give keramiekoven <bedrag>" <br />
* Als de oven helemaal afgekoeld is, terugbrengen naar de werkplaats, links onder in de stellage. Zorg dat de oven helemaal naar achteren zit, en niet over de gele lijn uitsteekt.<br />
<br />
== Oven gebruiken ==<br />
* Mail AUB naar de participantsonly@revspace.nl als je heel lang gaat stoken, gezien dat de sparkschack niet echt te gebruiken is in de tussentijd. <br />
* Oven langzaam uit zijn opslagplaats in de werkplaats rijden, let op dat je niet aan de stellingkast blijft hangen, er zitten uitstekende schroefjes aan de zijkanten<br />
* Rijd de oven naar de sparkshack, dit gaat makkelijker met twee personen.<br />
* Installeer de aansturing door eerst de stekker aan de achterkant te bevestigen, en vervolgens het thermokoppel in te steken. Je hebt keuze uit voor, achter en zijkant.<br />
* Zet de aansturingsunit NIET op de bovenkant, want daar wordt het heet.<br />
* Zorg dat je object niet direct op de bodem van de oven staat, en niet het verwarmingselement aanraakt/kortsluit<br />
* Stekker in het stopcontact, noteer de meterstand, zet de grote schakelaar aan.<br />
* Stel de gewenste temperatuur, stookcurve en timer in, leg de keramische platen over de bovenopening, en veel plezier<br />
* Noteer hieronder je ervaringen over wat, hoe veel, hoe lang en op welke stookcurves je uitkomt, zodat we ervaring opbouwen.<br />
<br />
== ToDos ==<br />
* Oven steunen en aantal platen regelen, zodat we meerdere dingen tegelijkertijd kunnen stoken. <br />
* Een plug gieten van gips/chamotte cement, zodat het afgesloten en geopend kan worden. <br />
* Stuk keramiekwol regelen, en bevestigen aan de onderkant, om die nog beter te isoleren. <br />
* Aansturing digitaal maken, zodat we willekeurige stook curves er in kunnen zetten<br />
* Staal van de bovenkant repareren of vervangen, buitenkant schuren en in hoog-temperatuur verf zetten, liefst knal rood ;)<br />
* Via Pwuts, mail van Broere uit Gouda: "Wij hebben in het verleden ovens gemaakt voor Vingerling. De productie van deze ovens is in 1999 gestaakt. Tot 2008 hebben wij nog service verleend aan de Vingerling ovens. In dat jaar hebben wij het verlenen van service overgedragen aan de firma Keramikos in Haarlem. Website: http://www.keramikos.nl/ . U kunt zich tot dat bedrijf wenden voor reparaties en onderhoud." - De Keramikos website ziet er niet 'hacker-onvriendelijk' uit, dus wellicht dat daar mogelijkheden zijn om informatie los te krijgen?<br />
<br />
== Ervaringen ==<br />
Er zijn meerdere soort dingen die we met de oven kunnen doen<br />
<br />
=== Keramiek bakken ===<br />
* ...<br />
<br />
=== Glas fusen ===<br />
* ...<br />
<br />
=== Metalen smelten ===<br />
* ...<br />
<br />
=== Glas / metaal annealen ===<br />
* ...<br />
<br />
=== ??? ===<br />
* ...</div>Luteijnhttps://revspace.nl/index.php?title=CO2MeterHacking&diff=17637CO2MeterHacking2018-02-19T12:33:30Z<p>Luteijn: /* Investigation & findings */ add link to another pdf describing info on a serial protocol</p>
<hr />
<div> {{Project<br />
|Name=CO2MeterHacking<br />
|Picture=Voltcraft-c100.jpg<br />
|Status=Completed<br />
|Contact= bertrik<br />
}}<br />
<br />
'''status: it works'''.<br />
We've modified the code to work using an ESP8266 which publishes the measurement data directly on an MQTT stream.<br />
<br />
The G, C, D, V signals from the CO2-sensor inside the CO-100 are routed internally to the RJ45-plug, so there's actually no need to open it and solder a connector on the G, C, D, V lines. <br />
<br />
== Introduction ==<br />
This project is about hacking the Voltcraft CO-100 CO<sub>2</sub>-meter, such that we can read the exact ppm value as displayed on the LCD.<br />
<br />
This particular CO<sub>2</sub>-meter is present in the main space of RevSpace.<br />
Having the CO<sub>2</sub> ppm value available as a number allows for nice things such as logging the levels over time, announce them on IRC, show them on the [[LedBanner]], fine-grained control of the ventilation system, etc.<br />
<br />
The user manual of the Voltcraft CO-100 says<br />
"Achtung! Der RJ45-Anschluss (siehe Kapitel 7, Position „K“) darf nicht verwendet werden. Der Anschluss ist nur für den Hersteller vorgesehen."<br />
Of course, a claim like that can only be interpreted as a challenge! :)<br />
<br />
See also:<br />
* http://www2.zyaura.com/support/manual/pdf/ZyAura_CO2_Monitor_Carbon_Dioxide_ZG01C%20Module%20English%20user%20manual_1404.pdf<br />
* http://www.palebluedot.nl/jml/projects/arduino/52-reading-data-from-the-voltcraft-co-100<br />
<br />
== Investigation & findings ==<br />
[[File:Co-100_overview.jpg|thumb|right|CO-100 internals]]<br />
The CO<sub>2</sub> sensor inside the CO-100 (in the left of the picture) has a sticker saying ZGw063RY.<br />
Googling for this number reveals a CO<sub>2</sub> module that looks just like the Voltcraft CO-100, so it appears that the CO-100 is basically a rebranded [http://www.zyaura.com/products/ZGw063.asp ZyAura ZGw063RY] module.<br />
<br />
The CO-100 seems to miss a bunch of components that can be mounted on the PCB, close to the RJ45 connection (most likely an RS232 chip with charge pump capacitors).<br />
[[File:Co-100.jpg|thumb|left|components around the RJ45 connector]]<br />
<br />
http://co2meters.com/Documentation/AppNotes/AN146-RAD-0401-serial-communication.pdf has some information on what looks like a clone/similar device, describing a serial cable for the RJ45, and the protocol used, as well as some info on re-calibration. Commands/Identifiers seem to match.<br />
<br />
The CO<sub>2</sub> sensor in the CO-100 is a [http://www.zyaura.com/products/ZG_module.asp ZyAura ZG-01 module].<br />
This sensor uses the ZyAura protocol, which vaguely resembles SPI, see [[File:ZyAura_CO2_Monitor_Carbon_Dioxide_ZG01_Module_english_manual-1.pdf|ZG01 CO<sub>2</sub> Monitor Module user manual]].<br />
<br />
On the bottom left of the PCB is a set of pads that are marked with G, C, D, V, meaning Ground, Clock, Data, Voltage of the ZG01 sensor.<br />
The voltage level on the clock and data pins is 3.3V (the voltage on V pin is 3.3V too).<br />
<br />
The ZG-01 sends 5-byte frames containing measurement values:<br />
* byte 0 is an identifier for the measurement item, e.g. whether it is a CO<sub>2</sub> ppm value or a temperature.<br />
* byte 1 and 2 contain the value of the item (byte 1 is the MSB, byte 2 is the LSB)<br />
* byte 3 is a checksum over bytes 0-2, just the sum modulo 256.<br />
* byte 4 is always 0x0D<br />
Besides the CO<sub>2</sub> ppm value and temperature, it also sends various other (so far unknown) measurement items.<br />
<br />
Measurement items encountered so far:<br />
<br />
{| class="wikitable"<br />
! Item<br />
! Value<br />
! Remark<br />
|-<br />
| 0x41 'A' || 3290 || Relative humidity in units of 0.01%<br />
|-<br />
| 0x42 'B' || 4708 || Temperature in Kelvin (unit of 1/16th K)<br />
|-<br />
| 0x43 'C' || 2964 || ?<br />
|-<br />
| 0x46 'F' || 6882 || Temperature in degrees Fahrenheit (unit of 0.01)?<br />
|-<br />
| 0x4F 'O' || 7754 || ?<br />
|-<br />
| 0x50 'P' || 857 || CO<sub>2</sub> concentration in ppm<br />
|-<br />
| 0x52 'R' || 10438 || Barometric pressure?<br />
|-<br />
| 0x56 'V' || 10443 || Barometric pressure?<br />
|-<br />
| 0x57 'W' || 7880 || ?<br />
|-<br />
| 0x6D 'm' || 2559 || Seems to always have same value<br />
|-<br />
| 0x6E 'n' || 17146 || ?<br />
|-<br />
| 0x71 'q' || 855 || Always close to value of item 0x50<br />
|}<br />
<br />
<br />
Pin-out of the 8P8C ("RJ45") connector on the side of the CO-100:<br />
{| class="wikitable"<br />
! T568B<br />
! Colour<br />
! Signal<br />
|-<br />
| 1 || orange-white || Supply voltage out, measured about 5.7V<br />
|-<br />
| 2 || orange || DATA, 3.3V level<br />
|-<br />
| 3 || green-white || CLOCK, 3.3V level<br />
|-<br />
| 4 || blue || GND<br />
|}<br />
<br />
== Hardware ==<br />
<br />
=== NRF version ===<br />
The ZG-01 module and NRF24L01+ transceiver are connected to the Arduino as follows:<br />
{| class="wikitable"<br />
! Arduino<br />
! Module<br />
! Remark<br />
|-<br />
| D2 || ZG-C || ZG01 clock signal<br />
|-<br />
| D3 || ZG-D || ZG01 data signal<br />
|-<br />
| GND || NRF-1, ZG-G || NRF ground, ZG01 ground<br />
|-<br />
| 3V3 || NRF-2 || NRF power<br />
|-<br />
| D8 || NRF-3 || NRF CE<br />
|-<br />
| D9 || NRF-4 || NRF CSN<br />
|-<br />
| D13 || NRF-5 || NRF SCK<br />
|-<br />
| D11 || NRF-6 || NRF MOSI<br />
|-<br />
| D12 || NRF-7 || NRF MISO<br />
|-<br />
| - || 8 || NRF IRQ - not connected<br />
|}<br />
<br />
=== ESP version ===<br />
In the ESP version of the CO2 meter, signals from the ZG-01 module are connected as follows:<br />
<br />
{| class="wikitable"<br />
! Wemos D1-mini<br />
! ZG01<br />
! Remark<br />
|-<br />
| D1 || ZG-C || ZG01 clock signal, 3.3V<br />
|-<br />
| D2 || ZG-D || ZG01 data signal, 3.3V<br />
|-<br />
| GND || ZG-G || ZG01 ground<br />
|-<br />
| 5V || ? || Supply voltage from 8P8C, about 6V!<br />
|}<br />
<br />
== Software ==<br />
The basic function of the software on the arduino is to monitor the C and D signals, until a CO<sub>2</sub> measurement is received from the ZG-01, then send it using the wireless transceiver.<br />
<br />
The source code can be found [https://github.com/bertrik/co2sensor on github].<br />
This archive contains two arduino projects:<br />
* one for an Arduino Pro Mini, using an NRF24L01+ for the wireless connection<br />
* one for an ESP8266, sending measurement values directly to MQTT over the WiFi.<br />
<br />
=== ZG-01 decoding ===<br />
The ZG-01 protocol is decoded using a simple state machine.<br />
On each falling edge of the clock line, a sample of the data line is taken until a total of 40 bits is received.<br />
If the time between a bit and the previous bit is longer than 2 milliseconds, it is assumed that a new 5-byte frame has started.<br />
<br />
The technical description for a very similar CO2 module: [http://www.zyaura.com/support/demo/ZG106_FW_001.ExtSpec.pdf ZG106 protocol].<br />
<br />
=== Wireless protocol ===<br />
<br />
==== NRF ====<br />
To control the NRF24L01+ wireless transceiver, we use the<br />
[https://github.com/gcopeland/RF24 gcopeland fork of the RF24 library].<br />
This library has several important fixes over the original RF24 library (and is used in the receiver as well).<br />
<br />
The wireless message consist of 7 bytes:<br />
* 0x06 "CO_2" <MSB> <LSB> for the CO<sub>2</sub> concentration message (in ppm)<br />
* 0x06 "HUMI" <MSB> <LSB> for the relative humidity message (in units of 0.01%)<br />
<br />
==== ESP ====<br />
The ESP version of the software uses MQTT to publish the measurement values, on the following topics (retained):<br />
* revspace/sensors/co2 with the CO2 concentration in ppm, e.g. "400 PPM"<br />
* revspace/sensors/humidity with the relative humidity in percent, e.g. "43.56 %"<br />
* revspace/sensors/temperature with the temperature in degrees Celcius, e.g. "23.9 °C"<br />
<br />
For example, running<br />
mosquitto_sub -h revspace.nl -t revspace/sensors/co2 -v<br />
produces output like<br />
revspace/sensors/co2 530 PPM<br />
revspace/sensors/co2 530 PPM<br />
revspace/sensors/co2 532 PPM<br />
revspace/sensors/co2 533 PPM<br />
<br />
=== Graphs and heat maps ===<br />
Interesting visualizations:<br />
* [http://keetweej.vanheusden.com/revspace/co2.php CO2 level heatmap]<br />
* [https://revgraph.bewaar.me/dashboard/db/all-co2?from=now-3h&to=now CO2 level graph]</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Vortex&diff=17632Luteijn/Vortex2018-02-16T10:22:03Z<p>Luteijn: /* Countdown Timer */</p>
<hr />
<div>===Project "Vortex":===<br />
{{Project<br />
|Name=Luteijn/Vortex<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=Vortex_1.jpg<br />
}}<br />
<br />
I've got four DFRobot 'Vortex' units. Basic programming via the official apps is possible, but rather limited. The WhenDo app is only available on iPad. Luckily, the internal Arduino clone can be directly programmed too. Unfortunately, not all the specs seem to be available, but there are some examples to work with, although they have a quite a bit of 'magic numbers' in them. These examples might disappear, so duplicating them here, with my own notes as available. The examples-coding is mostly done by "Andy Zhou <Andy.zhou@dfrobot.com>", although I did change some of the things to figure out what they do.<br />
<br />
http://wiki.dfrobot.com.cn/index.php?title=(SKU:ROB0116)_Vortex%E5%8F%AF%E7%BC%96%E7%A8%8B%E6%9C%BA%E5%99%A8%E4%BA%BA#.E6.A0.B7.E4.BE.8B.E4.BB.A3.E7.A0.81 <br />
<br />
https://www.dfrobot.com/wiki/index.php/Vortex_Arduino_Coding_Tutorial_V1.0#Introduction<br />
<br />
(Chinese page seems to have a bit better documentation, although English page might be easier to read and it is interesting to compare the differences)<br />
<br />
http://wiki.dfrobot.com.cn/images/1/10/%E4%B8%BB%E6%9D%BF.png<br />
<br />
==== What's connected to the pins ====<br />
{| class="wikitable"<br />
|+ Connection overview<br />
|-<br />
|0<br />
|Serial RX (input)<br />
|-<br />
|1<br />
|Serial TX (output)<br />
|-<br />
|2<br />
|Encoder Wheel (External Interrupt 0) (input)<br />
|-<br />
|3<br />
|Encoder Wheel (External Interrupt 1) (input)<br />
|-<br />
|4<br />
|Unknown, seems to be pulled high when used as input. Found it too hard to trace on the board.<br />
|-<br />
|5<br />
|Motor 0 Speed (PWM) (output)<br />
|-<br />
|6<br />
|Motor 1 Speed (PWM) (output)<br />
|-<br />
|7<br />
|IR decoder/detector (input)<br />
|-<br />
|8<br />
|IR led Left (output)<br />
|-<br />
|9<br />
|Motor 0 direction (H=forwards,L=reverse) (output)<br />
|-<br />
|10<br />
|Motor 1 direction (output)<br />
|-<br />
|11<br />
|MP3-player TX (output)<br />
|-<br />
|12<br />
|IR led Right (output)<br />
|-<br />
|13<br />
|GRB LED chain around body (output)<br />
|-<br />
|A0<br />
|grayscale 4 'D' (input)<br />
|-<br />
|A1<br />
|grayscale 3 'C' (input)<br />
|-<br />
|A2<br />
|grayscale 2 'B' (input)<br />
|-<br />
|A3<br />
|grayscale 1 'A' (input)<br />
|-<br />
|A4<br />
|SDA (i2c)<br />
|-<br />
|A5<br />
|SCL (i2c)<br />
|-<br />
|A6<br />
|grayscale 5 'E' (input)<br />
|-<br />
|A7<br />
|grayscale 6 'F' (input)<br />
|-<br />
|}<br />
<br />
i2c bus + Vcc & Gnd should be available at 4 pin rear expansion port too - need to find a suitable connector for it..<br />
<br />
http://www.hobbytronics.co.uk/arduino-atmega328-pinout Useful diagrams to map pins to package<br />
<br />
==== Dumping original firmware ====<br />
Although you can/should be able to reflash Vortex with the App, might as well make a backup of the orignal:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:r:"Vortex.hex":i<br />
This can be uploaded again with:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:w:"Vortex.hex":i<br />
(The app might also be initializing other things, but this seems to work)<br />
<br />
Your own sketches can be uploaded with <br />
arduino --upload MySketch.ino<br />
But using avrdude to upload the .hex file is a bit quicker.<br />
<br />
==== Motor Control ====<br />
<br />
Simple 'enable' and 'direction' pins for left and right wheels. Enable can be PWM'd or just full on.<br />
This example revs up the engines with PWM.<br />
<pre><br />
int E1 = 5; <br />
int M1 = 9; <br />
int E2 = 6; <br />
int M2 = 10; <br />
<br />
void setup() <br />
{ <br />
pinMode(M1, OUTPUT); // directional controls, High is forward, Low is backward <br />
pinMode(M2, OUTPUT); <br />
} <br />
<br />
void loop() <br />
{ <br />
int value; <br />
for(value = 0 ; value <= 255; value+=5) //foreward <br />
{ <br />
digitalWrite(M1,HIGH); <br />
digitalWrite(M2, HIGH); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
<br />
for(value = 0 ; value <= 255; value+=5) //backward <br />
{ <br />
digitalWrite(M1, LOW); <br />
digitalWrite(M2, LOW); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
} <br />
</pre><br />
<br />
====Encoders====<br />
The two external interrupts are linked to encoders that track wheel movement, so it is possible to detect the Vortext is being pushed/pulled/turned, although I don't think these things indicate which direction the robot is pushed in...<br />
<br />
<pre><br />
#define pinInputLeft 0 // this is the interrupt, not the pin number, D2<br />
#define pinInputRight 1 // external int 1, D3<br />
long leftPul,rightPul;<br />
<br />
void leftCallBack(){<br />
leftPul++;<br />
}<br />
<br />
void rightCallBack(){<br />
rightPul++;<br />
}<br />
<br />
void initDdevice(){<br />
pinMode(5,OUTPUT);<br />
pinMode(6,OUTPUT);<br />
pinMode(9,OUTPUT);<br />
pinMode(10,OUTPUT);<br />
noInterrupts();<br />
attachInterrupt(pinInputLeft,leftCallBack,CHANGE);<br />
attachInterrupt(pinInputRight,rightCallBack,CHANGE);<br />
interrupts();<br />
}<br />
<br />
void motorDebug(){<br />
digitalWrite(5,HIGH);<br />
digitalWrite(6,HIGH);<br />
digitalWrite(9,HIGH);<br />
digitalWrite(10,HIGH);<br />
}<br />
<br />
void printPul(){<br />
Serial.print(leftPul);<br />
Serial.print(" ");<br />
Serial.println(rightPul);<br />
leftPul = 0;<br />
rightPul = 0;<br />
}<br />
<br />
void setup() {<br />
initDdevice();<br />
Serial.begin(9600);<br />
motorDebug();<br />
}<br />
<br />
void loop() {<br />
printPul();<br />
delay(500);<br />
}<br />
</pre><br />
====Analogue Inputs====<br />
six 'grayscale' detectors are placed around the bottom of the Vortex, to be used to track a line, maybe read simple codes from the floor.<br />
<pre> <br />
2 (a2) 3 (a1) <br />
1 (a3) 4 (a0)<br />
<br />
<br />
<br />
<br />
5 (a6) 6 (a7)<br />
<br />
</pre><br />
This is the example sketch to dump the values read by the 6 little eyes.<br />
<pre><br />
void setup(void){<br />
Serial.begin(9600); <br />
}<br />
<br />
int analogBuf[6] = {'\0'};<br />
<br />
void loop(void){<br />
analogBuf[0] = analogRead(3);<br />
analogBuf[1] = analogRead(2);<br />
analogBuf[2] = analogRead(1);<br />
analogBuf[3] = analogRead(0);<br />
analogBuf[4] = analogRead(6);<br />
analogBuf[5] = analogRead(7);<br />
for(int i=0;i<6;i++){<br />
Serial.print(i+1);<br />
Serial.print(": ");<br />
Serial.print(analogBuf[i]);<br />
Serial.print(" ");<br />
}<br />
Serial.println();<br />
delay(500);<br />
}<br />
</pre><br />
<br />
Wonder what's connected to A4 and A5, if anything? These are used for the i2c bus! See below under the [[Luteijn/Vortex#Eyes]] section<br />
<br />
====LEDs====<br />
12 RGB (GRB!) leds can be addressed. Example from website causes Red and Green to be swapped. initialize library differently:<br />
<pre><br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
</pre><br />
IR example below uses some of the LED functionality.<br />
<br />
====IR obstacle detector====<br />
Seems to be somewhat flaky. Needs a bit more work to figure out. Also, the IR sensor is useful to read IR remote controllers or beacons. This receiver seems to work at 38kHz, but probabbly also reacts to 36-40kHz. Remote controllers often work at 36kHz. Note that 2x 8µs delay doesn't give you 38kHz, but the digitalWrite itself also induces quite some delay (3-6µs), so seems this is bringing the total period up to around the 26.something µs we'd expect.<br />
<br />
<br />
<pre><br />
#define NUM_LEDS 12 <br />
<br />
// Data pin that led data will be written out over <br />
#define DATA_PIN 13 <br />
CRGB leds[NUM_LEDS]; <br />
<br />
#define IR_IN 7//IR receiver pin <br />
#define L_IR 8 //left ir transmitter pin <br />
#define R_IR 12 //right ir transmitter pin <br />
<br />
int count; <br />
<br />
void leftSend38KHZ(void){//left ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(L_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(L_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
void rightSend38KHZ(void){//right ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(R_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(R_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
<br />
void pcint0Init(void){//init the interrupt <br />
PCICR |= 1 << PCIE2; <br />
PCMSK2 |= 1 << PCINT23; //pin D7 is int23 <br />
} <br />
<br />
ISR(PCINT2_vect){//IR decoder interrupt <br />
count++; <br />
} <br />
<br />
void obstacleAvoidance(void){ <br />
char i; <br />
count=0; <br />
leds[5] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){ //left transmitter sends 20 pulses <br />
leftSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[5] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Left"); <br />
delay(100); <br />
} <br />
count=0; <br />
leds[3] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){//right transmitter sends 20 pulses <br />
rightSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[3] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Right"); <br />
delay(100); <br />
} <br />
delay(600); <br />
} <br />
<br />
void setup(void){ <br />
delay(2000); <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
pinMode(L_IR,OUTPUT);//init the left transmitter pin <br />
pinMode(R_IR,OUTPUT);//init the right transmitter pin <br />
pinMode(IR_IN,INPUT);//init the ir receiver pin <br />
Serial.begin(9600); <br />
leds[4] = CRGB::Blue; <br />
FastLED.show(); <br />
delay(2000); <br />
leds[4] = CRGB::Purple; <br />
FastLED.show(); <br />
noInterrupts(); <br />
pcint0Init(); <br />
interrupts(); //enable the interrupt <br />
} <br />
<br />
void loop(void){ <br />
obstacleAvoidance(); <br />
}<br />
</pre><br />
<br />
==== IRreceiver ====<br />
Since DFrobot also have a 'loose' IR-receiver module in their program, decided to just see if the example sketches for those would transfer, and they do.<br />
https://www.dfrobot.com/product-366.html - A remote similar to the one pictured on the DFRobot product page actually came with one of my RTLSDR sticks, and seems to work well enough, although the range isn't great, couple of meters. With a 'proper' remote control, e.g. from a TV, the range is fine, 10m at least when pointing at unit from across the house. Might be less if using reflections to try to control a roving robot.<br />
<br />
So, using the IRremote library you can read values from a remote controller:<br />
<pre><br />
#include "IRremote.h"<br />
#define IR_IN 7//IR receiver pin<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
<br />
void setup(void){<br />
Serial.begin(9600); <br />
irrecv.enableIRIn(); // Start the receiver<br />
} <br />
<br />
void loop(void){ <br />
if (irrecv.decode(&results)) { <br />
Serial.println(results.value, HEX); <br />
irrecv.resume(); // Receive the next value<br />
} <br />
} <br />
</pre><br />
This was already useful to read codes from the remote of the AV-receiver we have at home and then play these back via a universal remote app on the smartphone that was missing some buttons. For some reason I couldn't find the 'learn' function in the app, but could enter the codes as read by this sketch easily.<br />
<br />
The receiver is mounted on the front, bottom. So not right between the eyes, although a spot seems to be prepared for it there too.<br />
<br />
The tables at [[Luteijn/IRRemotes]] might be useful when creating a sketch that is to be controlled over IR.<br />
<br />
Here's an example of a simple driving sketch controlled via my RTeL-Cheapo remote.<br />
<pre><br />
#include "IRremote.h"<br />
<br />
#define IR_IN (7)//IR receiver pin<br />
#define E1 (5)<br />
#define E2 (6)<br />
#define M1 (9)<br />
#define M2 (10)<br />
<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
// MSB is direction, 0xff full forward, 0x00 full backwards<br />
uint8_t Left=128; // 0x80 : forward, speed 0<br />
uint8_t Right=128; // 0x80 : forward, speed 0<br />
<br />
void setup(void){<br />
delay(2000); // grace period<br />
Serial.begin(9600);<br />
pinMode(M1, OUTPUT);<br />
pinMode(M2, OUTPUT);<br />
Engine(Left,Right);<br />
irrecv.enableIRIn(); // Start the receiver<br />
}<br />
<br />
void Engine(uint8_t Left, uint8_t Right) {<br />
uint8_t D1=Left&0x80;<br />
uint8_t D2=Right&0x80;<br />
uint8_t S1=D1?Left&0x7f:0x7f-(Left&0x7f);<br />
uint8_t S2=D2?Right&0x7f:0x7f-(Right&0x7f);<br />
Serial.print(Left,HEX);<br />
Serial.print("<-L R->");<br />
Serial.println(Right,HEX);<br />
digitalWrite(M1,D1);<br />
analogWrite(E1,S1<<1);<br />
digitalWrite(M2,D2);<br />
analogWrite(E2,S2<<1);<br />
}<br />
uint32_t last;<br />
void loop(void){<br />
uint32_t code;<br />
if (code=irrecv.decode(&results)) {<br />
code=results.value;<br />
Serial.print("Got IR:");<br />
Serial.println(code,HEX);<br />
<br />
if (code==0xFFFFFFFF) {code=last;}; // repeated press<br />
<br />
if (code==0xFFB24D) { // power = full stop<br />
Left=128;<br />
Right=128;<br />
} else if (code==0xFF02FD) { // full screen both go towards full stop<br />
if (Left<128) Left++; else Left--;<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFFA05F) { // ch+ increase both towards full ahead<br />
if (Left<255) Left++;<br />
if (Right<255) Right++;<br />
} else if (code==0xFF40BF) { // ch- decrease both towards full reverse<br />
if (Left>0) Left--;<br />
if (Right>0) Right--;<br />
} else if (code==0xFF50AF) { // vol- left towards full stop<br />
if (Left<128) Left++; else Left--;<br />
} else if (code==0xFF32CD) { // record increase left towards full ahead<br />
if (Left<255) Left++;<br />
} else if (code==0xFF48B7) { // 0 decrease left towards full reverse<br />
if (Left>0) Left--;<br />
} else if (code==0xFF7887) { // vol+ right towards full stop<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFF30CF) { // time shift increase right towards full ahead<br />
if (Right<255) Right++;<br />
} else if (code==0xFF38C7) { // recall decrease right towards full reverse<br />
if (Right>0) Right--;<br />
} else {<br />
// ignore unknown codes<br />
}<br />
last=code;<br />
irrecv.resume(); // Receive the next value<br />
Engine(Left,Right);<br />
}<br />
}<br />
</pre><br />
It would be better to use a stronger remote for this as it's hard to successfully control the robot when it's facing away from you. Also, the speed ramp up/down is a bit slow, and should probably take bigger steps than one at a time. <br />
<br />
Left as an exercise to the reader: add controls to spin in place left/right, using interrupts of wheel encoders to rotate 45/90 etc. degrees, boundary detection with analogue sensors, object detection with IR (careful not to make control harder). Switch to a bluetooth remote controller once figured out the possibilities of bluetooth better?<br />
<br />
====MP3-player====<br />
There is an MP3.player integrated in the robot. It can be controlled by sending more or less magic commands over software Serial via pin 11. Pin 2 might be connected to the player too if the example I found can be believed, but my tests so far never had anything received there, or on pin 4. Would be nice to get an 'end of song reached' or to get things like version information out. Anyway, Pin 2 is connected to one of the wheel encoders, so not likely to be the RX. It might also be co-connected to one of the relatively harmless outputs, like to the ir emitter? Will need to trace the board to find out, but initial quick look didn't immediately show anything.<br />
<br />
The example found on the DFRobot site:<br />
<pre><br />
#define MP3_VOLUME 0x10<br />
#define TX 11<br />
#define RX 4 // ?? was 2 in example I found, but that is unlikely<br />
#include <SoftwareSerial.h><br />
<br />
SoftwareSerial mySerial(RX, TX);// RX, TX<br />
void setup()<br />
{<br />
delay(1000); <br />
mp3Init();<br />
mp3setVolume(30);//0~30<br />
}<br />
void loop()<br />
{<br />
mp3player(1);<br />
delay(2000);<br />
mp3stop();<br />
delay(1000);<br />
}<br />
void mp3Init()<br />
{<br />
mySerial.begin (9600);<br />
}<br />
void mp3setVolume(byte vol)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
void mp3player(byte data)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
<br />
void mp3stop()<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x16, 0x00, 0x00, 0x00, 0xef};<br />
mySerial.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
The files to play can be put on the Vortex via usb, seems the little switch next to the micro-usb socket switches this between the mp3-players mass-storage and the arduino usb-serial interface. The number passed to the player is just the index into the (FAT?) table of stored songs. So, be careful of the order these are uploaded to the memory in. <br />
<br />
https://www.dfrobot.com/product-1121.html , documented at https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299 is a mini-MP3 player from DFRobot. It is controllable over serial, and might be the same one as used in the Vortex. Would be good to figure out if the 'busy' signal and/or the serial TX are connected back to the arduino somewhere in that case. Player could also be a variant but the magic numbers more or less match (although the stop command 0x16 is not in the table. <br />
<br />
The MP3-chip inside Vortex has Part# YX6100-24SS. Seems to be part of a family of serial MP3-players made by "广州悦欣电子科技有限公司" --> "Guangzhou Yue Xin Electronic Technology Co., Ltd." - website http://www.yx080.com/mp3xinpian/53-15.html has a download link on this yx6100 page but it seems to just refer us to the 5200 application Manual V1.8, which appears to be mostly compatible. The command table for the chip does include a 0x16 stop command. <br />
<br />
Also the datasheet mentions the chip can play both MP3 and WAV. YX5300-24SS is a similar part, datasheet at https://xp-dev.com/trac/arduino_antonio/export/280/arduino_antonio/trunk/EnterpriseBase/Serial%20MP3%20Player/About%20the%20Chip%20-%20YX5300/YX5300-24SS%20Datasheet%20V1.0.pdf The command table for that chip does include a 0x16 stop command.<br />
<br />
Documentation for the DFRobot miniplayer mentions the instruction format to be:<br />
{| class="wikitable"<br />
|+ Format: $S VER Len CMD Feedback para1 para2 checksum $O<br />
|-<br />
| $S<br />
| Start byte 0x7e<br />
| Each command begins with 0x7e ('~')<br />
|-<br />
| VER<br />
| Version information<br />
| Version seems to be 0xff from the example<br />
|-<br />
| Len<br />
| the number of bytes <br />
| Start, End and Checksum are not counted,<br />
|-<br />
| CMD<br />
| Commands<br />
| See command table.<br />
|-<br />
| Feedback<br />
| Command feedback<br />
| 1: feedback, 0: no feedback ; need to test what this does, might mean 'echo'<br />
|-<br />
| Para1<br />
| Parameter 1<br />
| Query high data byte<br />
|-<br />
| Para2<br />
| Parameter 2<br />
| Query low data byte<br />
|-<br />
| checksum<br />
| Checksum<br />
| accumulation and verification, doesn't include start byte. Seems to be 2 bytes from the example given in the docs, but Vortex seems to just not need it put in. example given is 7e ff 06 09 00 00 04 ff dd ef ; The 5200 Datasheet uses the exact same example command, and explains that you should add all the bytes, and then subtract those from 0 to get the checksum. The 5300 Datasheet also mentions "另外用户也可以直接忽视校验,参考我们的5.3.4 章节说明。" so looks like the checksum is optional if you don't care too much about corruption of the occasional command.<br />
|-<br />
| $O<br />
| End byte<br />
| End of command is signaled with 0xEF<br />
|}<br />
<br />
{| class="wikitable"<br />
|+Command table WIP to copy this over<br />
|-<br />
!CMD<br />
!Function Description<br />
!Parameters (16 bit)<br />
!compared with YX5300 info<br />
|-<br />
|0x01<br />
|Next<br />
|<br />
|-<br />
|0x02<br />
|Previous<br />
|<br />
|-<br />
|0x03<br />
|Specific track<br />
|0-2999 but example is only using low byte. also track 0 doesn't seem to do anything?<br />
|Indicates 1-255 are the valid tracks only.<br />
|-<br />
|0x04<br />
|Volume up<br />
|<br />
|-<br />
|0x05<br />
|Volume down<br />
|<br />
|-<br />
|0x06<br />
|Set Volume<br />
|0-30 (10 is already quite loud!)<br />
|-<br />
|0x07<br />
|Specify EQ(0/1/2/3/4/5/)<br />
|Normal/Pop/Rock/Jazz/Classic/Base.<br />
|Reserved in the 5300's command list.<br />
|-<br />
|0x08<br />
|Specify playback mode (0/1/2/3)<br />
|Repeat/folder repeat/single repeat/random<br />
|See 3.4.3; this explains the argument is the song to play in a loop - may have been changed for 6100<br />
|-<br />
|0x09<br />
|Select source (0/1/2/3/4)<br />
|U/TF/AUX/SLEEP/FLASH<br />
|See 3.4.4; specifies 1 as U, 2 as TF, 4 as PC, 5 FLASH and 6 SLEEP<br />
|-<br />
|0x0a<br />
|Enter into standby - low power loss<br />
|<br />
|Sleep - Low power consumption 10MA (obviously mA or µA, not MA, is meant)<br />
|-<br />
|0x0b<br />
|Normal working<br />
|<br />
|Wake up from sleep<br />
|-<br />
|0x0c<br />
|reset module<br />
|<br />
|chip reset<br />
|-<br />
|0x0d<br />
|Playback<br />
|<br />
|Play - seems to be contrast to suspend/pause<br />
|-<br />
|0x0e<br />
|Pause<br />
|<br />
|Suspended<br />
|-<br />
|0x0f<br />
|Specify folder to playback<br />
|1~10(need to set by user)<br />
|see 3.4.5 - DH: represents the name of the folder, the default support for 99 files, 01 - 99 named DL: represents the track, the default maximum of 255 songs, that is, 0x01 ~ 0xFF<br />
|-<br />
|0x10<br />
|Volume adjust set<br />
|DH=1:open volume adjust DL: set volume gain 0~31<br />
|not present in command table <br />
|-<br />
|0x11<br />
|Repeat play<br />
|1:start repeat play 0: stop play<br />
|not present in command table<br />
|-<br />
|0x16<br />
|not in the table<br />
|example mentions it as a stop command<br />
|Stop<br />
|-<br />
|0x17<br />
|not in the table<br />
|<br />
|FLASH only, see 3.4.7 (but is in section 3.4.6), select folder to loop.<br />
|-<br />
|0x18<br />
|not in the table<br />
|<br />
|Reserved<br />
|-<br />
|0x19<br />
|not in the table<br />
|<br />
|See 3.4.8 (but is in section 3.4.7), select loop current track mode<br />
|-<br />
|0x21<br />
|not in the table<br />
|<br />
|See 3.4.9 (3.4.8), set DAC to High-Z mode (1) or on (0) so you can use the amp for something else.<br />
|-<br />
|0x22<br />
|not in the table<br />
|<br />
|See 3.4.10 (section 3.4.9 doesn't exist), Set volume and song to play in one command (H:volume L:track)<br />
|-<br />
|0x3C<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3D<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3E<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3F<br />
|Send initialization parameters<br />
|0-0x0F (each bit represent one device of the low-four bits)<br />
|Seems to be reporting which storage devices are currently online (see section 3.5.1) <br />
|-<br />
|0x40<br />
|Returns an error, request retransmission<br />
|<br />
|Probably this is a response to a command meaning Error Encountered.<br />
|-<br />
|0x41<br />
|Answer<br />
|<br />
|Probably this is a response to a query and just means Accepted.<br />
|-<br />
|0x42<br />
|Query Status<br />
|<br />
|See 3.4.10 (actually 3.5.2) - explains that this will return which storage is used and if it's playing, stopped or paused. The storage could also be 'SLEEP' to indicate sleeping?<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
<br />
|}<br />
<br />
Code to calculate the checksum from the Library supporting the miniplayer:<br />
<pre><br />
uint16_t DFRobotDFPlayerMini::calculateCheckSum(uint8_t *buffer){<br />
uint16_t sum = 0;<br />
for (int i=Stack_Version; i<Stack_CheckSum; i++) {<br />
sum += buffer[i];<br />
}<br />
return -sum;<br />
}<br />
</pre><br />
but this doesn't seem to be needed.<br />
<br />
====Eyes====<br />
The 'Eyes' are connected over i2c. The daughterboard they are on features an STM8S103K3 (see http://www.st.com/en/microcontrollers/stm8s103-105.html ) and 2x HC595 (shift registers).<br />
Besides 35 predefined eye patterns, it is also possible to upload your own eye patterns.<br />
<br />
The default eyes can be set as follows:<br />
<pre><br />
#include <Wire.h><br />
#define I2C_LED_ADDRESS 0b1100000<br />
#define I2C_WRITE 0x00<br />
<br />
uint8_t serial=0;<br />
<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
defined_eyes(6,1); <br />
delay(2000);<br />
}<br />
void loop(){<br />
defined_eyes(1,serial); <br />
serial++; <br />
if(serial>=35) serial=0; <br />
delay(250); <br />
}<br />
<br />
<br />
void defined_eyes(uint8_t color,uint8_t serial) {<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4 <br />
// (I suppose they mean 0x40, although eyes seem to also respond on 0xC0 which is what is defined above, and 0x20)<br />
Wire.write(color&0x07); // color bits: 1 blue, 2 green, 4 red <br />
Wire.write(serial); //preset eyes 0~34<br />
Wire.endTransmission(); // stop transmitting <br />
}<br />
<br />
</pre><br />
<pre><br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21 <br />
//6 7 8 9 10 20 19 18 17 16 <br />
//11 12 13 14 15 15 14 13 12 11 <br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1 <br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4<br />
Wire.write(0x55); // color 55=custom eyes follow <br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
// other values cause eyes to light up in multiple colors, needs more work<br />
<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R of foreground<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
} <br />
Wire.endTransmission(); // stop transmitting <br />
<br />
<br />
void example_eyes() {<br />
/* left eye of robot (right for onlooker) */<br />
Wire.write(0x1f); // bottom row, all bits lit<br />
Wire.write(0x1e); // 10 9 8 7 on, 6 off<br />
Wire.write(0x1c);<br />
Wire.write(0x18);<br />
Wire.write(0x10);<br />
<br />
/* right eye of robot (left for onlooker) */<br />
Wire.write(0x1f); // top row all 5 bits lit<br />
Wire.write(0x0f); // leds 6,7,8,9 on 10 off<br />
Wire.write(0x07); // 11,12,13 on, 14,15 off <br />
Wire.write(0x03); <br />
Wire.write(0x01); <br />
} <br />
</pre><br />
<br />
=====KITT-scanner / Cylon=====<br />
This animates the eyes and adds some simple sound effects - currently the eye warble is not synchronized to the eye movement. The IR remote is used to trigger the well-known 'By your command'. For a KITT scanner may want to change to the proper woosh for a KITT, and replace the command sample with something like 'yes, Michael' :)<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <SoftwareSerial.h><br />
#include <IRremote.h><br />
<br />
#define MTX (11)<br />
#define MRX (4)<br />
<br />
// Song number for "By your command"-sample<br />
#define BYC (30)<br />
// Song number for Warble<br />
#define Warble (31)<br />
<br />
#define IR_IN (7)<br />
<br />
#define I2C_LED_ADDRESS 0x80<br />
bool left=1;<br />
decode_results results;<br />
SoftwareSerial mp3(MRX,MTX);<br />
IRrecv irrecv(IR_IN);<br />
short command=0;<br />
<br />
uint8_t eyes[10]={0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00};<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
mp3Init();<br />
irrecv.enableIRIn(); // Start the receiver<br />
delay(2000);<br />
delay(100);<br />
mp3setVolume(10);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(2); // repeat once<br />
delay(50);<br />
mp3player(BYC);<br />
delay(3000);<br />
mp3setVolume(8);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
delay(50);<br />
}<br />
void lscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]<<1);<br />
eyes[7]=(uint8_t)(eyes[7]<<1);<br />
}<br />
<br />
void rscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]>>1);<br />
eyes[7]=(uint8_t)(eyes[7]>>1);<br />
}<br />
<br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
<br />
// 1 2 4 8 10 '20' 10 8 4 2 1<br />
if (left) {lscroll_eyes(eyes);}<br />
else if (!left) {rscroll_eyes(eyes);}<br />
<br />
custom_eyes(4,eyes);<br />
<br />
if (eyes[7]==0x1) {left=1;}<br />
else if (eyes[2]==0x1) {left=1;}<br />
else if (left && eyes[2]>=0x10) {eyes[2]=0;eyes[7]=0x20;left=0;}<br />
else if (left && eyes[7]>=0x10) {eyes[7]=0;eyes[2]=0x20;left=0;}<br />
if (irrecv.decode(&results)){<br />
if (command) {<br />
command=0;<br />
} else{<br />
command=34; // adjust to match sample length<br />
mp3player(BYC);<br />
delay(50);<br />
}<br />
irrecv.resume();<br />
} else {<br />
if (command) {<br />
command--;<br />
if (!command){<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
} else {<br />
delay(80);<br />
}<br />
} else {<br />
delay(80);<br />
}<br />
}<br />
}<br />
<br />
void mp3Init()<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x0C, 0x00, 0x00, 0x00, 0xef};<br />
mp3.begin(9600);<br />
delay(100);<br />
mp3.write(buffer, 8);<br />
delay(100);<br />
}<br />
void mp3setVolume(byte vol)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3player(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3repeat(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x08, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
===== Conway's Game of Life =====<br />
2 5x5 fields linked as a torus of 10x5. Change mapping of cells in the edge to make e.g. a front and a back side, or change to 8x8 and add 4 more faces and a bit more mapping to create an LED cube of 6 faces. Simple animation on the 2 eye matrices. This version doesn't actually do something with the remote control, but it was easy to add things like reset board, change color, introduce disturbance etc.<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <IRremote.h><br />
<br />
#define IR_IN (7)<br />
#define I2C_LED_ADDRESS 0x80<br />
<br />
// 5x5 matrix eye<br />
#define CX (5)<br />
#define CY (5)<br />
// edge of 1 cell on each end<br />
#define CXM (7)<br />
#define CYM (7)<br />
<br />
#define F (2) // 2 faces have I.<br />
<br />
decode_results results;<br />
IRrecv irrecv(IR_IN);<br />
<br />
uint8_t eyes[10];<br />
<br />
uint8_t board_a[F][CYM][CXM];<br />
uint8_t board_b[F][CYM][CXM];<br />
<br />
<br />
inline void clear(uint8_t board[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
for (k=0;k<F;k++) {<br />
for (j=0;j<CYM;j++) {<br />
for (i=0;i<CXM;i++) {<br />
board[k][j][i]=0;<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t neighbors(uint8_t s[][CYM][CXM], uint8_t k,uint8_t j,uint8_t i) {<br />
return s[k][j-1][i-1]+s[k][j-1][i]+s[k][j-1][i+1]+s[k][j][i-1]+s[k][j][i+1]+s[k][j+1][i-1]+s[k][j+1][i]+s[k][j+1][i+1];<br />
}<br />
<br />
inline void play(uint8_t s[][CYM][CXM], uint8_t d[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
// TODO: load edges from right faces hardcoded for 5x5 now<br />
for (i=1;i<=5;i++) {<br />
s[0][0][i]=s[0][5][i];<br />
s[0][6][i]=s[0][1][i];<br />
<br />
s[0][i][0]=s[1][i][5];<br />
s[0][i][6]=s[1][i][1];<br />
<br />
s[1][0][i]=s[1][5][i];<br />
s[1][6][i]=s[1][1][i];<br />
<br />
s[1][i][0]=s[0][i][5];<br />
s[1][i][6]=s[0][i][1];<br />
<br />
}<br />
// corners<br />
s[0][0][0]=s[1][5][5];<br />
s[0][6][0]=s[1][1][5];<br />
<br />
s[0][0][6]=s[1][5][1];<br />
s[0][6][6]=s[1][1][1];<br />
<br />
s[1][0][0]=s[0][5][5];<br />
s[1][6][0]=s[0][1][5];<br />
<br />
s[1][0][6]=s[0][5][1];<br />
s[1][6][6]=s[0][1][1];<br />
<br />
<br />
// update interior cells<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
switch (neighbors(s,k,j,i)) {<br />
case 2: d[k][j][i]=s[k][j][i]; // remain<br />
break;<br />
case 3: d[k][j][i]=1; // birth<br />
break;<br />
default: d[k][j][i]=0; // death<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t convert_l(uint8_t l[CXM]) {<br />
return (l[5]|l[4]<<1|l[3]<<2|l[2]<<3|l[1]<<4);<br />
}<br />
inline uint8_t convert_r(uint8_t l[CXM]) {<br />
return (l[1]|l[2]<<1|l[3]<<2|l[4]<<3|l[5]<<4);<br />
}<br />
<br />
inline uint8_t * board_to_eyes(uint8_t board[F][CYM][CXM]) {<br />
static uint8_t eyes[10]={0,0,0,0,0,0,0,0,0,0};<br />
eyes[0]=convert_l(board[1][5]);<br />
eyes[1]=convert_l(board[1][4]);<br />
eyes[2]=convert_l(board[1][3]);<br />
eyes[3]=convert_l(board[1][2]);<br />
eyes[4]=convert_l(board[1][1]);<br />
<br />
eyes[5]=convert_r(board[0][1]);<br />
eyes[6]=convert_r(board[0][2]);<br />
eyes[7]=convert_r(board[0][3]);<br />
eyes[8]=convert_r(board[0][4]);<br />
eyes[9]=convert_r(board[0][5]);<br />
<br />
return eyes;<br />
}<br />
<br />
<br />
void dump(uint8_t b[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
Serial.println("==");<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
Serial.print(b[k][j][i]?'x':'o');<br />
}<br />
Serial.println("");<br />
} <br />
Serial.println("==");<br />
}<br />
<br />
}<br />
<br />
void setup(){<br />
delay(2000);<br />
Serial.begin(9600);<br />
Serial.println("life");<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
irrecv.enableIRIn(); // Start the receiver<br />
clear(board_a); // start with empty board a<br />
clear(board_b); // start with empty board b<br />
//some initial state:<br />
board_a[0][1][2]=1;<br />
board_a[0][2][3]=1;<br />
board_a[0][3][1]=1;<br />
board_a[0][3][2]=1;<br />
board_a[0][3][3]=1;<br />
board_a[1][1][2]=1;<br />
board_a[1][2][3]=1;<br />
board_a[1][3][1]=1;<br />
board_a[1][3][2]=1;<br />
board_a[1][3][3]=1;<br />
Serial.println("inited");<br />
}<br />
<br />
inline void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21<br />
//6 7 8 9 10 20 19 18 17 16<br />
//11 12 13 14 15 15 14 13 12 11<br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
// dump(board_a);<br />
custom_eyes(4,board_to_eyes(board_a));<br />
play(board_a,board_b);<br />
delay(50);<br />
// dump(board_b);<br />
custom_eyes(4,board_to_eyes(board_b));<br />
play(board_b,board_a);<br />
delay(50);<br />
<br />
//TODO: add something to react to remote here, e.g. set some cells etc.<br />
if (irrecv.decode(&results)){<br />
irrecv.resume();<br />
} else {<br />
}<br />
}<br />
<br />
</pre><br />
<br />
===== Countdown Timer =====<br />
<br />
This sketch turns Vortex into an eggtimer to be used to keep track of how much time you have left when giving a talk, or before your food is ready. It displays the time left on the eyes, and can be controlled with a IR remote to (re)set the timer. LED colors are adjusted based on the time left, with vortex blinking red when you're out of time. <br />
<br />
Below the timer is set up to count minutes, but you can of course change the number of milliseconds per 'tick' to count seconds or whatever. Adding things like rotating lights to indicate progress of time or playing sound effects if desired are left to the reader, this is more or less a demo of using the eyes as a digit display. <br />
<br />
Because the two eyes are rotated 180 degrees compared to each other, there is code to swap bit-patterns (for the full 5 pixels per row, even if the digits here only use 3 per row). The font is rotated at run-time in setup(), so it's easier to tweak the font. <br />
<br />
Make sure to restart Vortex in time for the 55 day limit on the millis() roll-over not to affect things, or add code to detect and handle the rollover.<br />
<br />
The code to work with a remote is set up for a specific remote controller (RC 240 in TV mode), so you probably have to adjust this bit to work with the controller you intend to use.<br />
<br />
<pre><br />
#include <Wire.h> <br />
#include <IRremote.h> <br />
#include <FastLED.h> <br />
//IR receiver pin <br />
#define IR_IN (7) <br />
#define I2C_LED_ADDRESS 0b100000 <br />
#define I2C_WRITE 0x00 <br />
// How many leds are in the strip? <br />
#define NUM_LEDS 12 <br />
// Data pin that led data will be written out over <br />
#define DATA_PIN 13 <br />
CRGB leds[NUM_LEDS]; <br />
// how many milliseconds per count (1000=count seconds, 60000 count minutes) <br />
unsigned long MperC=60000; <br />
<br />
unsigned long start; <br />
short countdown; <br />
short displayed; <br />
uint8_t state=0; <br />
unsigned long m_elapsed; <br />
short c_elapsed; <br />
unsigned long long current=0xffffffff; <br />
unsigned long long last=0xffffffff; <br />
<br />
IRrecv irrecv(IR_IN); <br />
decode_results results; <br />
<br />
uint8_t eyes[10]={0,0,0,0,0,0,0,0,0,0}; <br />
<br />
uint8_t left_digit[10][5]={ <br />
{0x04,0x0A,0x0A,0x0A,0x04}, //0 <br />
{0x0E,0x04,0x04,0x0C,0x04}, //1 <br />
{0x0E,0x08,0x04,0x02,0x0C}, //2 <br />
{0x0C,0x02,0x0C,0x02,0x0C}, //3 <br />
{0x02,0x02,0x0E,0x0A,0x0A}, //4 <br />
{0x0E,0x02,0x0C,0x08,0x0E}, //5 <br />
{0x04,0x0A,0x0C,0x08,0x04}, //6 <br />
{0x04,0x04,0x02,0x02,0x0C}, //7 <br />
{0x04,0x0A,0x04,0x0A,0x04}, //8 <br />
{0x04,0x02,0x06,0x0A,0x04} //9 <br />
}; <br />
uint8_t right_digit[10][5]; <br />
uint8_t swap(uint8_t l) { <br />
switch (l&0x1f){ <br />
case 0x0: return 0; <br />
case 0x01: return 0x10; <br />
case 0x02: return 0x08; <br />
case 0x03: return 0x18; <br />
case 0x04: return 0x04; <br />
case 0x05: return 0x14; <br />
case 0x06: return 0x0C; <br />
case 0x07: return 0x1C; <br />
case 0x08: return 0x02; <br />
case 0x09: return 0x12; <br />
case 0x0a: return 0x0A; <br />
case 0x0b: return 0x1A; <br />
case 0x0c: return 0x06; <br />
case 0x0d: return 0x16; <br />
case 0x0e: return 0x0E; <br />
case 0x0f: return 0x1E; <br />
case 0x10: return 0x01; <br />
case 0x11: return 0x11; <br />
case 0x12: return 0x09; <br />
case 0x13: return 0x19; <br />
case 0x14: return 0x05; <br />
case 0x15: return 0x15; <br />
case 0x16: return 0x0D; <br />
case 0x17: return 0x1d; <br />
case 0x18: return 0x03; <br />
case 0x19: return 0x13; <br />
case 0x1a: return 0x0B; <br />
case 0x1b: return 0x1B; <br />
case 0x1c: return 0x07; <br />
case 0x1d: return 0x17; <br />
case 0x1e: return 0x0F; <br />
case 0x1f: return 0x1F; <br />
default: return 0; <br />
} <br />
} <br />
void setup(){ <br />
char i,j; <br />
delay(2000); <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
for (i=0;i<10;i++){ <br />
for (j=0;j<5;j++) { <br />
right_digit[i][j]=swap(left_digit[i][4-j]); <br />
} <br />
} <br />
irrecv.enableIRIn(); <br />
Wire.begin(); // join i2c bus (address optional for master) <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
for (i=0;i<12;i++){ <br />
leds[i]=CRGB::Green; <br />
} <br />
FastLED.show(); <br />
start=millis(); <br />
displayed=100; <br />
countdown=99; <br />
state=0; <br />
}<br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){ <br />
uint8_t index; <br />
// right eye left eye <br />
//1 2 3 4 5 25 24 23 22 21 <br />
//6 7 8 9 10 20 19 18 17 16 <br />
//11 12 13 14 15 15 14 13 12 11 <br />
//16 17 18 19 20 10 9 8 7 6 <br />
//21 22 23 24 25 5 4 3 2 1 <br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4 <br />
Wire.write(0x55); // color 55=custom eyes follow <br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line <br />
Wire.write(color&0x7); //color 1-B,2-G,4-R <br />
<br />
for (index=0;index<10;index++) { <br />
Wire.write(eyeline[index]); <br />
} <br />
Wire.endTransmission(); // stop transmitting <br />
// A9 = some sort of multicolor mode <br />
// AA = one color byte, then 10 bytes defining the 2 eyes. <br />
// AB = some sort of multicolor mode <br />
} <br />
void update_display(short displayed) { <br />
byte h=displayed/10; <br />
byte l=displayed%10; <br />
eyes[0]=left_digit[l][0]; <br />
eyes[1]=left_digit[l][1]; <br />
eyes[2]=left_digit[l][2]; <br />
eyes[3]=left_digit[l][3]; <br />
eyes[4]=left_digit[l][4]; <br />
eyes[5]=right_digit[h][0]; <br />
eyes[6]=right_digit[h][1]; <br />
eyes[7]=right_digit[h][2]; <br />
eyes[8]=right_digit[h][3]; <br />
eyes[9]=right_digit[h][4]; <br />
if (displayed<5) { <br />
custom_eyes(4,eyes); <br />
} else { <br />
custom_eyes(2,eyes); <br />
} <br />
} <br />
<br />
void update_leds(short displayed) { <br />
char i; <br />
if (displayed>5) { <br />
for (i=0;i<12;i++){ <br />
leds[i]=CRGB(0,displayed*2,0); <br />
} <br />
} else if (displayed>0) { <br />
for (i=0;i<12;i++){ <br />
leds[i]=0xff9900; <br />
} <br />
} else if (state==1) { <br />
for (i=0;i<6;i++) { <br />
leds[i]=CRGB::Red; <br />
leds[i+6]=CRGB::Black; <br />
} <br />
state=0; <br />
} else if (state==0) { <br />
for (i=0;i<6;i++) { <br />
leds[i+6]=CRGB::Red; <br />
leds[i]=CRGB::Black; <br />
} <br />
state=1; <br />
} <br />
FastLED.show(); <br />
}<br />
void loop(){ <br />
unsigned long m_elapsed=millis()-start; <br />
// FIXME handle overflow of millis. <br />
short c_elapsed=(m_elapsed/MperC); <br />
if (displayed>0) { <br />
if (countdown-c_elapsed!=displayed) { <br />
displayed=countdown-c_elapsed; <br />
displayed=displayed<0?0:displayed; <br />
update_display(displayed); <br />
} <br />
} <br />
update_leds(displayed); <br />
//TODO check IR commands <br />
if (irrecv.decode(&results)) { <br />
current=results.value; <br />
if (current!=last) { <br />
last=current; // store current key to be able to recognize repeats <br />
current=current&0xfffff7ff; // filter out repeat bit. This is for RC5. <br />
<br />
if (current==0xa) { // 'clear' on my particular remote <br />
countdown=0; <br />
} else if (current==0xf) { // 'memo' <br />
start=millis(); // reference time is now <br />
displayed=99; // restarts timer with current countdown setting <br />
} else if (current==0x0) { // 'digit 0', not all remotes might be this regular <br />
digit(0); <br />
} else if (current==0x1) { <br />
digit(1); <br />
} else if (current==0x2) { <br />
digit(2); <br />
} else if (current==0x3) { <br />
digit(3); <br />
} else if (current==0x4) { <br />
digit(4); <br />
} else if (current==0x5) { <br />
digit(5); <br />
} else if (current==0x6) { <br />
digit(6); <br />
} else if (current==0x7) { <br />
digit(7); <br />
} else if (current==0x8) { <br />
digit(8); <br />
} else if (current==0x9) { <br />
digit(9); <br />
} else if (current==0x0) { // TODO add pause, up/down by 1 <br />
} <br />
} <br />
irrecv.resume(); // Receive the next value <br />
} <br />
<br />
delay(500); <br />
} <br />
void digit(byte d) { <br />
displayed=0; // stops timer updates to display <br />
countdown=(countdown%10)*10+d; <br />
update_display(countdown); <br />
}<br />
<br />
</pre><br />
<br />
==== i2c ====<br />
There might be more stuff connected to the i2c already, I plan to do some tests to find out. Would be nice if e.g. the flash of the mp3 player or some other expansion was already there.<br />
<br />
Regardless of what's there already, Vortex seems to be designed to be extended over i2c, and could be a nice platform to do some experiments/demo's with. <br />
<br />
Here's a simple scanning sketch, looks like only 0x40 has something connected, and that would be the eye-display.<br />
<br />
<pre><br />
#include <Wire.h><br />
<br />
<br />
void setup()<br />
{<br />
Wire.begin();<br />
<br />
Serial.begin(9600);<br />
while (!Serial); // Leonardo: wait for serial monitor<br />
Serial.println("\nI2C Scanner");<br />
}<br />
<br />
<br />
void loop()<br />
{<br />
byte error, address;<br />
int nDevices;<br />
<br />
Serial.println("Scanning...");<br />
<br />
nDevices = 0;<br />
for(address = 0; address < 128; address++ )<br />
{<br />
// The i2c_scanner uses the return value of<br />
// the Write.endTransmisstion to see if<br />
// a device did acknowledge to the address.<br />
Wire.beginTransmission(address);<br />
error = Wire.endTransmission();<br />
<br />
if (error == 0)<br />
{<br />
Serial.print("I2C device found at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
<br />
nDevices++;<br />
}<br />
else if (error==4)<br />
{<br />
Serial.print("Unknown error at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
} <br />
}<br />
if (nDevices == 0)<br />
Serial.println("No I2C devices found\n");<br />
else<br />
Serial.println("done\n");<br />
<br />
delay(5000); // wait 5 seconds for next scan<br />
}<br />
</pre><br />
<br />
====Bluetooth====<br />
It seems the Bluetooth functionality is the same as for the "Bluno" and thus the information at https://www.dfrobot.com/wiki/index.php/Bluno_SKU:DFR0267#Wireless_Programming_via_BLE applies.<br />
<br />
The AT commands seem to work, up to a point, but looks like it is a somewhat variant version of the firmware as AT+VERSION command doesn't work. Updating program seem not available for Linux.<br />
<br />
Booting the Vortex with the 'boot' button (near UL7, Vortex bottom-right-back RGB led.), two leds blink, and linux detects the Vortex over USB as:<br />
[230656.359259] usb 1-1.1: new full-speed USB device number 103 using xhci_hcd<br />
[230656.488502] usb 1-1.1: New USB device found, idVendor=2341, idProduct=0043<br />
[230656.488508] usb 1-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[230656.488511] usb 1-1.1: Product: DFRobot Boot CDC<br />
[230656.488513] usb 1-1.1: Manufacturer: DFRobot BLUno Boot<br />
[230656.490491] cdc_acm 1-1.1:1.0: ttyACM0: USB ACM device<br />
<br />
Similarly the little BLE dongle comes up as:<br />
[232626.931574] usb 1-1.3: new full-speed USB device number 108 using xhci_hcd<br />
[232627.254357] usb 1-1.3: New USB device found, idVendor=2341, idProduct=0043<br />
[232627.254362] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[232627.265758] cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device <br />
<br />
If Vortex pairs with the dongle, it effectiely gains an additional, wireless, serial link to a different port on the host. It's connected to the same Serial port on the internal Arduino. Make sure to adjust speed setting to what is used in the currently running sketch, the default 115200 won't work if Serial.begin(9600) is already called..<br />
<br />
<br />
Using AT commands it should be possible to set up the link connect to other Vortex robots too.<br />
<br />
Need to do some experiments to see if it is possible to connect to a generic Bluetooth 4.x dongle too.<br />
<br />
====Expansion====<br />
[[file:Vortex expansion holes drawing.svg|right|frame|hole arangement seen from top, vortex facing left or right]]<br />
There are 2 sets of 4 positions for screws/bolts at the top of the vortex, around the battery compartment, obviously to be used to put an expansion unit on top of Vortex, which could be linked to the i2c bus.<br />
<br />
=====Dimensions=====<br />
<br />
The bigger set of holes are 65 mm apart from left to right and 55 from front to back (center to center). These seem to be meant for 3 mm machine screws<br />
<br />
The smaller set of holes are 2.5" apart from left to right and 2.75" from front to back (center to center). These seem to be meant for small screws biting into the plastic.<br />
<br />
===== The 4 pin expansion port=====<br />
Q: Does Vortex support sensor module expansion?<br />
A: Vortex has a “Maker mode” where you can connect sensors via its IIC/TWI interface.<br />
<br />
It would seem the only logical place for this is the 4 pin connector in the back. Looking at the connector from the back of Vortex:<br />
____<br />
|1234|<br />
-__-<br />
<br />
These pins seem to be connected to the rainbow cable coming up from the main PCB as follows:<br />
<br />
{| class="wikitable"<br />
|+Vortex expansion port<br />
|-<br />
!Pin!!Function!!Color<br />
|-<br />
|1||SDA?||~15kOhm to Blue<br />
|-<br />
|2||SCL?||Blue<br />
|-<br />
|3||Gnd?||Black<br />
|-<br />
|4||Vcc?||Red<br />
|-<br />
|}<br />
<br />
It would make sense for Red to be 5V, Black to be Gnd, and the other two SCL and SDA of the i2c bus. This needs some further testing, preferably with a known good i2c device connected to the expansion port. The ~15kOhm between what I suspect to be SCL and SDA would then be two ~7.5kOhm to some common 5 V point that is buffered from the 'Red' cable.</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Vortex&diff=17631Luteijn/Vortex2018-02-16T10:10:53Z<p>Luteijn: /* Countdown Timer */ to keep track of time left for presentation or use as an eggtimer</p>
<hr />
<div>===Project "Vortex":===<br />
{{Project<br />
|Name=Luteijn/Vortex<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=Vortex_1.jpg<br />
}}<br />
<br />
I've got four DFRobot 'Vortex' units. Basic programming via the official apps is possible, but rather limited. The WhenDo app is only available on iPad. Luckily, the internal Arduino clone can be directly programmed too. Unfortunately, not all the specs seem to be available, but there are some examples to work with, although they have a quite a bit of 'magic numbers' in them. These examples might disappear, so duplicating them here, with my own notes as available. The examples-coding is mostly done by "Andy Zhou <Andy.zhou@dfrobot.com>", although I did change some of the things to figure out what they do.<br />
<br />
http://wiki.dfrobot.com.cn/index.php?title=(SKU:ROB0116)_Vortex%E5%8F%AF%E7%BC%96%E7%A8%8B%E6%9C%BA%E5%99%A8%E4%BA%BA#.E6.A0.B7.E4.BE.8B.E4.BB.A3.E7.A0.81 <br />
<br />
https://www.dfrobot.com/wiki/index.php/Vortex_Arduino_Coding_Tutorial_V1.0#Introduction<br />
<br />
(Chinese page seems to have a bit better documentation, although English page might be easier to read and it is interesting to compare the differences)<br />
<br />
http://wiki.dfrobot.com.cn/images/1/10/%E4%B8%BB%E6%9D%BF.png<br />
<br />
==== What's connected to the pins ====<br />
{| class="wikitable"<br />
|+ Connection overview<br />
|-<br />
|0<br />
|Serial RX (input)<br />
|-<br />
|1<br />
|Serial TX (output)<br />
|-<br />
|2<br />
|Encoder Wheel (External Interrupt 0) (input)<br />
|-<br />
|3<br />
|Encoder Wheel (External Interrupt 1) (input)<br />
|-<br />
|4<br />
|Unknown, seems to be pulled high when used as input. Found it too hard to trace on the board.<br />
|-<br />
|5<br />
|Motor 0 Speed (PWM) (output)<br />
|-<br />
|6<br />
|Motor 1 Speed (PWM) (output)<br />
|-<br />
|7<br />
|IR decoder/detector (input)<br />
|-<br />
|8<br />
|IR led Left (output)<br />
|-<br />
|9<br />
|Motor 0 direction (H=forwards,L=reverse) (output)<br />
|-<br />
|10<br />
|Motor 1 direction (output)<br />
|-<br />
|11<br />
|MP3-player TX (output)<br />
|-<br />
|12<br />
|IR led Right (output)<br />
|-<br />
|13<br />
|GRB LED chain around body (output)<br />
|-<br />
|A0<br />
|grayscale 4 'D' (input)<br />
|-<br />
|A1<br />
|grayscale 3 'C' (input)<br />
|-<br />
|A2<br />
|grayscale 2 'B' (input)<br />
|-<br />
|A3<br />
|grayscale 1 'A' (input)<br />
|-<br />
|A4<br />
|SDA (i2c)<br />
|-<br />
|A5<br />
|SCL (i2c)<br />
|-<br />
|A6<br />
|grayscale 5 'E' (input)<br />
|-<br />
|A7<br />
|grayscale 6 'F' (input)<br />
|-<br />
|}<br />
<br />
i2c bus + Vcc & Gnd should be available at 4 pin rear expansion port too - need to find a suitable connector for it..<br />
<br />
http://www.hobbytronics.co.uk/arduino-atmega328-pinout Useful diagrams to map pins to package<br />
<br />
==== Dumping original firmware ====<br />
Although you can/should be able to reflash Vortex with the App, might as well make a backup of the orignal:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:r:"Vortex.hex":i<br />
This can be uploaded again with:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:w:"Vortex.hex":i<br />
(The app might also be initializing other things, but this seems to work)<br />
<br />
Your own sketches can be uploaded with <br />
arduino --upload MySketch.ino<br />
But using avrdude to upload the .hex file is a bit quicker.<br />
<br />
==== Motor Control ====<br />
<br />
Simple 'enable' and 'direction' pins for left and right wheels. Enable can be PWM'd or just full on.<br />
This example revs up the engines with PWM.<br />
<pre><br />
int E1 = 5; <br />
int M1 = 9; <br />
int E2 = 6; <br />
int M2 = 10; <br />
<br />
void setup() <br />
{ <br />
pinMode(M1, OUTPUT); // directional controls, High is forward, Low is backward <br />
pinMode(M2, OUTPUT); <br />
} <br />
<br />
void loop() <br />
{ <br />
int value; <br />
for(value = 0 ; value <= 255; value+=5) //foreward <br />
{ <br />
digitalWrite(M1,HIGH); <br />
digitalWrite(M2, HIGH); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
<br />
for(value = 0 ; value <= 255; value+=5) //backward <br />
{ <br />
digitalWrite(M1, LOW); <br />
digitalWrite(M2, LOW); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
} <br />
</pre><br />
<br />
====Encoders====<br />
The two external interrupts are linked to encoders that track wheel movement, so it is possible to detect the Vortext is being pushed/pulled/turned, although I don't think these things indicate which direction the robot is pushed in...<br />
<br />
<pre><br />
#define pinInputLeft 0 // this is the interrupt, not the pin number, D2<br />
#define pinInputRight 1 // external int 1, D3<br />
long leftPul,rightPul;<br />
<br />
void leftCallBack(){<br />
leftPul++;<br />
}<br />
<br />
void rightCallBack(){<br />
rightPul++;<br />
}<br />
<br />
void initDdevice(){<br />
pinMode(5,OUTPUT);<br />
pinMode(6,OUTPUT);<br />
pinMode(9,OUTPUT);<br />
pinMode(10,OUTPUT);<br />
noInterrupts();<br />
attachInterrupt(pinInputLeft,leftCallBack,CHANGE);<br />
attachInterrupt(pinInputRight,rightCallBack,CHANGE);<br />
interrupts();<br />
}<br />
<br />
void motorDebug(){<br />
digitalWrite(5,HIGH);<br />
digitalWrite(6,HIGH);<br />
digitalWrite(9,HIGH);<br />
digitalWrite(10,HIGH);<br />
}<br />
<br />
void printPul(){<br />
Serial.print(leftPul);<br />
Serial.print(" ");<br />
Serial.println(rightPul);<br />
leftPul = 0;<br />
rightPul = 0;<br />
}<br />
<br />
void setup() {<br />
initDdevice();<br />
Serial.begin(9600);<br />
motorDebug();<br />
}<br />
<br />
void loop() {<br />
printPul();<br />
delay(500);<br />
}<br />
</pre><br />
====Analogue Inputs====<br />
six 'grayscale' detectors are placed around the bottom of the Vortex, to be used to track a line, maybe read simple codes from the floor.<br />
<pre> <br />
2 (a2) 3 (a1) <br />
1 (a3) 4 (a0)<br />
<br />
<br />
<br />
<br />
5 (a6) 6 (a7)<br />
<br />
</pre><br />
This is the example sketch to dump the values read by the 6 little eyes.<br />
<pre><br />
void setup(void){<br />
Serial.begin(9600); <br />
}<br />
<br />
int analogBuf[6] = {'\0'};<br />
<br />
void loop(void){<br />
analogBuf[0] = analogRead(3);<br />
analogBuf[1] = analogRead(2);<br />
analogBuf[2] = analogRead(1);<br />
analogBuf[3] = analogRead(0);<br />
analogBuf[4] = analogRead(6);<br />
analogBuf[5] = analogRead(7);<br />
for(int i=0;i<6;i++){<br />
Serial.print(i+1);<br />
Serial.print(": ");<br />
Serial.print(analogBuf[i]);<br />
Serial.print(" ");<br />
}<br />
Serial.println();<br />
delay(500);<br />
}<br />
</pre><br />
<br />
Wonder what's connected to A4 and A5, if anything? These are used for the i2c bus! See below under the [[Luteijn/Vortex#Eyes]] section<br />
<br />
====LEDs====<br />
12 RGB (GRB!) leds can be addressed. Example from website causes Red and Green to be swapped. initialize library differently:<br />
<pre><br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
</pre><br />
IR example below uses some of the LED functionality.<br />
<br />
====IR obstacle detector====<br />
Seems to be somewhat flaky. Needs a bit more work to figure out. Also, the IR sensor is useful to read IR remote controllers or beacons. This receiver seems to work at 38kHz, but probabbly also reacts to 36-40kHz. Remote controllers often work at 36kHz. Note that 2x 8µs delay doesn't give you 38kHz, but the digitalWrite itself also induces quite some delay (3-6µs), so seems this is bringing the total period up to around the 26.something µs we'd expect.<br />
<br />
<br />
<pre><br />
#define NUM_LEDS 12 <br />
<br />
// Data pin that led data will be written out over <br />
#define DATA_PIN 13 <br />
CRGB leds[NUM_LEDS]; <br />
<br />
#define IR_IN 7//IR receiver pin <br />
#define L_IR 8 //left ir transmitter pin <br />
#define R_IR 12 //right ir transmitter pin <br />
<br />
int count; <br />
<br />
void leftSend38KHZ(void){//left ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(L_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(L_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
void rightSend38KHZ(void){//right ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(R_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(R_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
<br />
void pcint0Init(void){//init the interrupt <br />
PCICR |= 1 << PCIE2; <br />
PCMSK2 |= 1 << PCINT23; //pin D7 is int23 <br />
} <br />
<br />
ISR(PCINT2_vect){//IR decoder interrupt <br />
count++; <br />
} <br />
<br />
void obstacleAvoidance(void){ <br />
char i; <br />
count=0; <br />
leds[5] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){ //left transmitter sends 20 pulses <br />
leftSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[5] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Left"); <br />
delay(100); <br />
} <br />
count=0; <br />
leds[3] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){//right transmitter sends 20 pulses <br />
rightSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[3] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Right"); <br />
delay(100); <br />
} <br />
delay(600); <br />
} <br />
<br />
void setup(void){ <br />
delay(2000); <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
pinMode(L_IR,OUTPUT);//init the left transmitter pin <br />
pinMode(R_IR,OUTPUT);//init the right transmitter pin <br />
pinMode(IR_IN,INPUT);//init the ir receiver pin <br />
Serial.begin(9600); <br />
leds[4] = CRGB::Blue; <br />
FastLED.show(); <br />
delay(2000); <br />
leds[4] = CRGB::Purple; <br />
FastLED.show(); <br />
noInterrupts(); <br />
pcint0Init(); <br />
interrupts(); //enable the interrupt <br />
} <br />
<br />
void loop(void){ <br />
obstacleAvoidance(); <br />
}<br />
</pre><br />
<br />
==== IRreceiver ====<br />
Since DFrobot also have a 'loose' IR-receiver module in their program, decided to just see if the example sketches for those would transfer, and they do.<br />
https://www.dfrobot.com/product-366.html - A remote similar to the one pictured on the DFRobot product page actually came with one of my RTLSDR sticks, and seems to work well enough, although the range isn't great, couple of meters. With a 'proper' remote control, e.g. from a TV, the range is fine, 10m at least when pointing at unit from across the house. Might be less if using reflections to try to control a roving robot.<br />
<br />
So, using the IRremote library you can read values from a remote controller:<br />
<pre><br />
#include "IRremote.h"<br />
#define IR_IN 7//IR receiver pin<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
<br />
void setup(void){<br />
Serial.begin(9600); <br />
irrecv.enableIRIn(); // Start the receiver<br />
} <br />
<br />
void loop(void){ <br />
if (irrecv.decode(&results)) { <br />
Serial.println(results.value, HEX); <br />
irrecv.resume(); // Receive the next value<br />
} <br />
} <br />
</pre><br />
This was already useful to read codes from the remote of the AV-receiver we have at home and then play these back via a universal remote app on the smartphone that was missing some buttons. For some reason I couldn't find the 'learn' function in the app, but could enter the codes as read by this sketch easily.<br />
<br />
The receiver is mounted on the front, bottom. So not right between the eyes, although a spot seems to be prepared for it there too.<br />
<br />
The tables at [[Luteijn/IRRemotes]] might be useful when creating a sketch that is to be controlled over IR.<br />
<br />
Here's an example of a simple driving sketch controlled via my RTeL-Cheapo remote.<br />
<pre><br />
#include "IRremote.h"<br />
<br />
#define IR_IN (7)//IR receiver pin<br />
#define E1 (5)<br />
#define E2 (6)<br />
#define M1 (9)<br />
#define M2 (10)<br />
<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
// MSB is direction, 0xff full forward, 0x00 full backwards<br />
uint8_t Left=128; // 0x80 : forward, speed 0<br />
uint8_t Right=128; // 0x80 : forward, speed 0<br />
<br />
void setup(void){<br />
delay(2000); // grace period<br />
Serial.begin(9600);<br />
pinMode(M1, OUTPUT);<br />
pinMode(M2, OUTPUT);<br />
Engine(Left,Right);<br />
irrecv.enableIRIn(); // Start the receiver<br />
}<br />
<br />
void Engine(uint8_t Left, uint8_t Right) {<br />
uint8_t D1=Left&0x80;<br />
uint8_t D2=Right&0x80;<br />
uint8_t S1=D1?Left&0x7f:0x7f-(Left&0x7f);<br />
uint8_t S2=D2?Right&0x7f:0x7f-(Right&0x7f);<br />
Serial.print(Left,HEX);<br />
Serial.print("<-L R->");<br />
Serial.println(Right,HEX);<br />
digitalWrite(M1,D1);<br />
analogWrite(E1,S1<<1);<br />
digitalWrite(M2,D2);<br />
analogWrite(E2,S2<<1);<br />
}<br />
uint32_t last;<br />
void loop(void){<br />
uint32_t code;<br />
if (code=irrecv.decode(&results)) {<br />
code=results.value;<br />
Serial.print("Got IR:");<br />
Serial.println(code,HEX);<br />
<br />
if (code==0xFFFFFFFF) {code=last;}; // repeated press<br />
<br />
if (code==0xFFB24D) { // power = full stop<br />
Left=128;<br />
Right=128;<br />
} else if (code==0xFF02FD) { // full screen both go towards full stop<br />
if (Left<128) Left++; else Left--;<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFFA05F) { // ch+ increase both towards full ahead<br />
if (Left<255) Left++;<br />
if (Right<255) Right++;<br />
} else if (code==0xFF40BF) { // ch- decrease both towards full reverse<br />
if (Left>0) Left--;<br />
if (Right>0) Right--;<br />
} else if (code==0xFF50AF) { // vol- left towards full stop<br />
if (Left<128) Left++; else Left--;<br />
} else if (code==0xFF32CD) { // record increase left towards full ahead<br />
if (Left<255) Left++;<br />
} else if (code==0xFF48B7) { // 0 decrease left towards full reverse<br />
if (Left>0) Left--;<br />
} else if (code==0xFF7887) { // vol+ right towards full stop<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFF30CF) { // time shift increase right towards full ahead<br />
if (Right<255) Right++;<br />
} else if (code==0xFF38C7) { // recall decrease right towards full reverse<br />
if (Right>0) Right--;<br />
} else {<br />
// ignore unknown codes<br />
}<br />
last=code;<br />
irrecv.resume(); // Receive the next value<br />
Engine(Left,Right);<br />
}<br />
}<br />
</pre><br />
It would be better to use a stronger remote for this as it's hard to successfully control the robot when it's facing away from you. Also, the speed ramp up/down is a bit slow, and should probably take bigger steps than one at a time. <br />
<br />
Left as an exercise to the reader: add controls to spin in place left/right, using interrupts of wheel encoders to rotate 45/90 etc. degrees, boundary detection with analogue sensors, object detection with IR (careful not to make control harder). Switch to a bluetooth remote controller once figured out the possibilities of bluetooth better?<br />
<br />
====MP3-player====<br />
There is an MP3.player integrated in the robot. It can be controlled by sending more or less magic commands over software Serial via pin 11. Pin 2 might be connected to the player too if the example I found can be believed, but my tests so far never had anything received there, or on pin 4. Would be nice to get an 'end of song reached' or to get things like version information out. Anyway, Pin 2 is connected to one of the wheel encoders, so not likely to be the RX. It might also be co-connected to one of the relatively harmless outputs, like to the ir emitter? Will need to trace the board to find out, but initial quick look didn't immediately show anything.<br />
<br />
The example found on the DFRobot site:<br />
<pre><br />
#define MP3_VOLUME 0x10<br />
#define TX 11<br />
#define RX 4 // ?? was 2 in example I found, but that is unlikely<br />
#include <SoftwareSerial.h><br />
<br />
SoftwareSerial mySerial(RX, TX);// RX, TX<br />
void setup()<br />
{<br />
delay(1000); <br />
mp3Init();<br />
mp3setVolume(30);//0~30<br />
}<br />
void loop()<br />
{<br />
mp3player(1);<br />
delay(2000);<br />
mp3stop();<br />
delay(1000);<br />
}<br />
void mp3Init()<br />
{<br />
mySerial.begin (9600);<br />
}<br />
void mp3setVolume(byte vol)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
void mp3player(byte data)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
<br />
void mp3stop()<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x16, 0x00, 0x00, 0x00, 0xef};<br />
mySerial.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
The files to play can be put on the Vortex via usb, seems the little switch next to the micro-usb socket switches this between the mp3-players mass-storage and the arduino usb-serial interface. The number passed to the player is just the index into the (FAT?) table of stored songs. So, be careful of the order these are uploaded to the memory in. <br />
<br />
https://www.dfrobot.com/product-1121.html , documented at https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299 is a mini-MP3 player from DFRobot. It is controllable over serial, and might be the same one as used in the Vortex. Would be good to figure out if the 'busy' signal and/or the serial TX are connected back to the arduino somewhere in that case. Player could also be a variant but the magic numbers more or less match (although the stop command 0x16 is not in the table. <br />
<br />
The MP3-chip inside Vortex has Part# YX6100-24SS. Seems to be part of a family of serial MP3-players made by "广州悦欣电子科技有限公司" --> "Guangzhou Yue Xin Electronic Technology Co., Ltd." - website http://www.yx080.com/mp3xinpian/53-15.html has a download link on this yx6100 page but it seems to just refer us to the 5200 application Manual V1.8, which appears to be mostly compatible. The command table for the chip does include a 0x16 stop command. <br />
<br />
Also the datasheet mentions the chip can play both MP3 and WAV. YX5300-24SS is a similar part, datasheet at https://xp-dev.com/trac/arduino_antonio/export/280/arduino_antonio/trunk/EnterpriseBase/Serial%20MP3%20Player/About%20the%20Chip%20-%20YX5300/YX5300-24SS%20Datasheet%20V1.0.pdf The command table for that chip does include a 0x16 stop command.<br />
<br />
Documentation for the DFRobot miniplayer mentions the instruction format to be:<br />
{| class="wikitable"<br />
|+ Format: $S VER Len CMD Feedback para1 para2 checksum $O<br />
|-<br />
| $S<br />
| Start byte 0x7e<br />
| Each command begins with 0x7e ('~')<br />
|-<br />
| VER<br />
| Version information<br />
| Version seems to be 0xff from the example<br />
|-<br />
| Len<br />
| the number of bytes <br />
| Start, End and Checksum are not counted,<br />
|-<br />
| CMD<br />
| Commands<br />
| See command table.<br />
|-<br />
| Feedback<br />
| Command feedback<br />
| 1: feedback, 0: no feedback ; need to test what this does, might mean 'echo'<br />
|-<br />
| Para1<br />
| Parameter 1<br />
| Query high data byte<br />
|-<br />
| Para2<br />
| Parameter 2<br />
| Query low data byte<br />
|-<br />
| checksum<br />
| Checksum<br />
| accumulation and verification, doesn't include start byte. Seems to be 2 bytes from the example given in the docs, but Vortex seems to just not need it put in. example given is 7e ff 06 09 00 00 04 ff dd ef ; The 5200 Datasheet uses the exact same example command, and explains that you should add all the bytes, and then subtract those from 0 to get the checksum. The 5300 Datasheet also mentions "另外用户也可以直接忽视校验,参考我们的5.3.4 章节说明。" so looks like the checksum is optional if you don't care too much about corruption of the occasional command.<br />
|-<br />
| $O<br />
| End byte<br />
| End of command is signaled with 0xEF<br />
|}<br />
<br />
{| class="wikitable"<br />
|+Command table WIP to copy this over<br />
|-<br />
!CMD<br />
!Function Description<br />
!Parameters (16 bit)<br />
!compared with YX5300 info<br />
|-<br />
|0x01<br />
|Next<br />
|<br />
|-<br />
|0x02<br />
|Previous<br />
|<br />
|-<br />
|0x03<br />
|Specific track<br />
|0-2999 but example is only using low byte. also track 0 doesn't seem to do anything?<br />
|Indicates 1-255 are the valid tracks only.<br />
|-<br />
|0x04<br />
|Volume up<br />
|<br />
|-<br />
|0x05<br />
|Volume down<br />
|<br />
|-<br />
|0x06<br />
|Set Volume<br />
|0-30 (10 is already quite loud!)<br />
|-<br />
|0x07<br />
|Specify EQ(0/1/2/3/4/5/)<br />
|Normal/Pop/Rock/Jazz/Classic/Base.<br />
|Reserved in the 5300's command list.<br />
|-<br />
|0x08<br />
|Specify playback mode (0/1/2/3)<br />
|Repeat/folder repeat/single repeat/random<br />
|See 3.4.3; this explains the argument is the song to play in a loop - may have been changed for 6100<br />
|-<br />
|0x09<br />
|Select source (0/1/2/3/4)<br />
|U/TF/AUX/SLEEP/FLASH<br />
|See 3.4.4; specifies 1 as U, 2 as TF, 4 as PC, 5 FLASH and 6 SLEEP<br />
|-<br />
|0x0a<br />
|Enter into standby - low power loss<br />
|<br />
|Sleep - Low power consumption 10MA (obviously mA or µA, not MA, is meant)<br />
|-<br />
|0x0b<br />
|Normal working<br />
|<br />
|Wake up from sleep<br />
|-<br />
|0x0c<br />
|reset module<br />
|<br />
|chip reset<br />
|-<br />
|0x0d<br />
|Playback<br />
|<br />
|Play - seems to be contrast to suspend/pause<br />
|-<br />
|0x0e<br />
|Pause<br />
|<br />
|Suspended<br />
|-<br />
|0x0f<br />
|Specify folder to playback<br />
|1~10(need to set by user)<br />
|see 3.4.5 - DH: represents the name of the folder, the default support for 99 files, 01 - 99 named DL: represents the track, the default maximum of 255 songs, that is, 0x01 ~ 0xFF<br />
|-<br />
|0x10<br />
|Volume adjust set<br />
|DH=1:open volume adjust DL: set volume gain 0~31<br />
|not present in command table <br />
|-<br />
|0x11<br />
|Repeat play<br />
|1:start repeat play 0: stop play<br />
|not present in command table<br />
|-<br />
|0x16<br />
|not in the table<br />
|example mentions it as a stop command<br />
|Stop<br />
|-<br />
|0x17<br />
|not in the table<br />
|<br />
|FLASH only, see 3.4.7 (but is in section 3.4.6), select folder to loop.<br />
|-<br />
|0x18<br />
|not in the table<br />
|<br />
|Reserved<br />
|-<br />
|0x19<br />
|not in the table<br />
|<br />
|See 3.4.8 (but is in section 3.4.7), select loop current track mode<br />
|-<br />
|0x21<br />
|not in the table<br />
|<br />
|See 3.4.9 (3.4.8), set DAC to High-Z mode (1) or on (0) so you can use the amp for something else.<br />
|-<br />
|0x22<br />
|not in the table<br />
|<br />
|See 3.4.10 (section 3.4.9 doesn't exist), Set volume and song to play in one command (H:volume L:track)<br />
|-<br />
|0x3C<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3D<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3E<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3F<br />
|Send initialization parameters<br />
|0-0x0F (each bit represent one device of the low-four bits)<br />
|Seems to be reporting which storage devices are currently online (see section 3.5.1) <br />
|-<br />
|0x40<br />
|Returns an error, request retransmission<br />
|<br />
|Probably this is a response to a command meaning Error Encountered.<br />
|-<br />
|0x41<br />
|Answer<br />
|<br />
|Probably this is a response to a query and just means Accepted.<br />
|-<br />
|0x42<br />
|Query Status<br />
|<br />
|See 3.4.10 (actually 3.5.2) - explains that this will return which storage is used and if it's playing, stopped or paused. The storage could also be 'SLEEP' to indicate sleeping?<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
<br />
|}<br />
<br />
Code to calculate the checksum from the Library supporting the miniplayer:<br />
<pre><br />
uint16_t DFRobotDFPlayerMini::calculateCheckSum(uint8_t *buffer){<br />
uint16_t sum = 0;<br />
for (int i=Stack_Version; i<Stack_CheckSum; i++) {<br />
sum += buffer[i];<br />
}<br />
return -sum;<br />
}<br />
</pre><br />
but this doesn't seem to be needed.<br />
<br />
====Eyes====<br />
The 'Eyes' are connected over i2c. The daughterboard they are on features an STM8S103K3 (see http://www.st.com/en/microcontrollers/stm8s103-105.html ) and 2x HC595 (shift registers).<br />
Besides 35 predefined eye patterns, it is also possible to upload your own eye patterns.<br />
<br />
The default eyes can be set as follows:<br />
<pre><br />
#include <Wire.h><br />
#define I2C_LED_ADDRESS 0b1100000<br />
#define I2C_WRITE 0x00<br />
<br />
uint8_t serial=0;<br />
<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
defined_eyes(6,1); <br />
delay(2000);<br />
}<br />
void loop(){<br />
defined_eyes(1,serial); <br />
serial++; <br />
if(serial>=35) serial=0; <br />
delay(250); <br />
}<br />
<br />
<br />
void defined_eyes(uint8_t color,uint8_t serial) {<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4 <br />
// (I suppose they mean 0x40, although eyes seem to also respond on 0xC0 which is what is defined above, and 0x20)<br />
Wire.write(color&0x07); // color bits: 1 blue, 2 green, 4 red <br />
Wire.write(serial); //preset eyes 0~34<br />
Wire.endTransmission(); // stop transmitting <br />
}<br />
<br />
</pre><br />
<pre><br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21 <br />
//6 7 8 9 10 20 19 18 17 16 <br />
//11 12 13 14 15 15 14 13 12 11 <br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1 <br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4<br />
Wire.write(0x55); // color 55=custom eyes follow <br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
// other values cause eyes to light up in multiple colors, needs more work<br />
<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R of foreground<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
} <br />
Wire.endTransmission(); // stop transmitting <br />
<br />
<br />
void example_eyes() {<br />
/* left eye of robot (right for onlooker) */<br />
Wire.write(0x1f); // bottom row, all bits lit<br />
Wire.write(0x1e); // 10 9 8 7 on, 6 off<br />
Wire.write(0x1c);<br />
Wire.write(0x18);<br />
Wire.write(0x10);<br />
<br />
/* right eye of robot (left for onlooker) */<br />
Wire.write(0x1f); // top row all 5 bits lit<br />
Wire.write(0x0f); // leds 6,7,8,9 on 10 off<br />
Wire.write(0x07); // 11,12,13 on, 14,15 off <br />
Wire.write(0x03); <br />
Wire.write(0x01); <br />
} <br />
</pre><br />
<br />
=====KITT-scanner / Cylon=====<br />
This animates the eyes and adds some simple sound effects - currently the eye warble is not synchronized to the eye movement. The IR remote is used to trigger the well-known 'By your command'. For a KITT scanner may want to change to the proper woosh for a KITT, and replace the command sample with something like 'yes, Michael' :)<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <SoftwareSerial.h><br />
#include <IRremote.h><br />
<br />
#define MTX (11)<br />
#define MRX (4)<br />
<br />
// Song number for "By your command"-sample<br />
#define BYC (30)<br />
// Song number for Warble<br />
#define Warble (31)<br />
<br />
#define IR_IN (7)<br />
<br />
#define I2C_LED_ADDRESS 0x80<br />
bool left=1;<br />
decode_results results;<br />
SoftwareSerial mp3(MRX,MTX);<br />
IRrecv irrecv(IR_IN);<br />
short command=0;<br />
<br />
uint8_t eyes[10]={0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00};<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
mp3Init();<br />
irrecv.enableIRIn(); // Start the receiver<br />
delay(2000);<br />
delay(100);<br />
mp3setVolume(10);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(2); // repeat once<br />
delay(50);<br />
mp3player(BYC);<br />
delay(3000);<br />
mp3setVolume(8);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
delay(50);<br />
}<br />
void lscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]<<1);<br />
eyes[7]=(uint8_t)(eyes[7]<<1);<br />
}<br />
<br />
void rscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]>>1);<br />
eyes[7]=(uint8_t)(eyes[7]>>1);<br />
}<br />
<br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
<br />
// 1 2 4 8 10 '20' 10 8 4 2 1<br />
if (left) {lscroll_eyes(eyes);}<br />
else if (!left) {rscroll_eyes(eyes);}<br />
<br />
custom_eyes(4,eyes);<br />
<br />
if (eyes[7]==0x1) {left=1;}<br />
else if (eyes[2]==0x1) {left=1;}<br />
else if (left && eyes[2]>=0x10) {eyes[2]=0;eyes[7]=0x20;left=0;}<br />
else if (left && eyes[7]>=0x10) {eyes[7]=0;eyes[2]=0x20;left=0;}<br />
if (irrecv.decode(&results)){<br />
if (command) {<br />
command=0;<br />
} else{<br />
command=34; // adjust to match sample length<br />
mp3player(BYC);<br />
delay(50);<br />
}<br />
irrecv.resume();<br />
} else {<br />
if (command) {<br />
command--;<br />
if (!command){<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
} else {<br />
delay(80);<br />
}<br />
} else {<br />
delay(80);<br />
}<br />
}<br />
}<br />
<br />
void mp3Init()<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x0C, 0x00, 0x00, 0x00, 0xef};<br />
mp3.begin(9600);<br />
delay(100);<br />
mp3.write(buffer, 8);<br />
delay(100);<br />
}<br />
void mp3setVolume(byte vol)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3player(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3repeat(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x08, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
===== Conway's Game of Life =====<br />
2 5x5 fields linked as a torus of 10x5. Change mapping of cells in the edge to make e.g. a front and a back side, or change to 8x8 and add 4 more faces and a bit more mapping to create an LED cube of 6 faces. Simple animation on the 2 eye matrices. This version doesn't actually do something with the remote control, but it was easy to add things like reset board, change color, introduce disturbance etc.<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <IRremote.h><br />
<br />
#define IR_IN (7)<br />
#define I2C_LED_ADDRESS 0x80<br />
<br />
// 5x5 matrix eye<br />
#define CX (5)<br />
#define CY (5)<br />
// edge of 1 cell on each end<br />
#define CXM (7)<br />
#define CYM (7)<br />
<br />
#define F (2) // 2 faces have I.<br />
<br />
decode_results results;<br />
IRrecv irrecv(IR_IN);<br />
<br />
uint8_t eyes[10];<br />
<br />
uint8_t board_a[F][CYM][CXM];<br />
uint8_t board_b[F][CYM][CXM];<br />
<br />
<br />
inline void clear(uint8_t board[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
for (k=0;k<F;k++) {<br />
for (j=0;j<CYM;j++) {<br />
for (i=0;i<CXM;i++) {<br />
board[k][j][i]=0;<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t neighbors(uint8_t s[][CYM][CXM], uint8_t k,uint8_t j,uint8_t i) {<br />
return s[k][j-1][i-1]+s[k][j-1][i]+s[k][j-1][i+1]+s[k][j][i-1]+s[k][j][i+1]+s[k][j+1][i-1]+s[k][j+1][i]+s[k][j+1][i+1];<br />
}<br />
<br />
inline void play(uint8_t s[][CYM][CXM], uint8_t d[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
// TODO: load edges from right faces hardcoded for 5x5 now<br />
for (i=1;i<=5;i++) {<br />
s[0][0][i]=s[0][5][i];<br />
s[0][6][i]=s[0][1][i];<br />
<br />
s[0][i][0]=s[1][i][5];<br />
s[0][i][6]=s[1][i][1];<br />
<br />
s[1][0][i]=s[1][5][i];<br />
s[1][6][i]=s[1][1][i];<br />
<br />
s[1][i][0]=s[0][i][5];<br />
s[1][i][6]=s[0][i][1];<br />
<br />
}<br />
// corners<br />
s[0][0][0]=s[1][5][5];<br />
s[0][6][0]=s[1][1][5];<br />
<br />
s[0][0][6]=s[1][5][1];<br />
s[0][6][6]=s[1][1][1];<br />
<br />
s[1][0][0]=s[0][5][5];<br />
s[1][6][0]=s[0][1][5];<br />
<br />
s[1][0][6]=s[0][5][1];<br />
s[1][6][6]=s[0][1][1];<br />
<br />
<br />
// update interior cells<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
switch (neighbors(s,k,j,i)) {<br />
case 2: d[k][j][i]=s[k][j][i]; // remain<br />
break;<br />
case 3: d[k][j][i]=1; // birth<br />
break;<br />
default: d[k][j][i]=0; // death<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t convert_l(uint8_t l[CXM]) {<br />
return (l[5]|l[4]<<1|l[3]<<2|l[2]<<3|l[1]<<4);<br />
}<br />
inline uint8_t convert_r(uint8_t l[CXM]) {<br />
return (l[1]|l[2]<<1|l[3]<<2|l[4]<<3|l[5]<<4);<br />
}<br />
<br />
inline uint8_t * board_to_eyes(uint8_t board[F][CYM][CXM]) {<br />
static uint8_t eyes[10]={0,0,0,0,0,0,0,0,0,0};<br />
eyes[0]=convert_l(board[1][5]);<br />
eyes[1]=convert_l(board[1][4]);<br />
eyes[2]=convert_l(board[1][3]);<br />
eyes[3]=convert_l(board[1][2]);<br />
eyes[4]=convert_l(board[1][1]);<br />
<br />
eyes[5]=convert_r(board[0][1]);<br />
eyes[6]=convert_r(board[0][2]);<br />
eyes[7]=convert_r(board[0][3]);<br />
eyes[8]=convert_r(board[0][4]);<br />
eyes[9]=convert_r(board[0][5]);<br />
<br />
return eyes;<br />
}<br />
<br />
<br />
void dump(uint8_t b[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
Serial.println("==");<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
Serial.print(b[k][j][i]?'x':'o');<br />
}<br />
Serial.println("");<br />
} <br />
Serial.println("==");<br />
}<br />
<br />
}<br />
<br />
void setup(){<br />
delay(2000);<br />
Serial.begin(9600);<br />
Serial.println("life");<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
irrecv.enableIRIn(); // Start the receiver<br />
clear(board_a); // start with empty board a<br />
clear(board_b); // start with empty board b<br />
//some initial state:<br />
board_a[0][1][2]=1;<br />
board_a[0][2][3]=1;<br />
board_a[0][3][1]=1;<br />
board_a[0][3][2]=1;<br />
board_a[0][3][3]=1;<br />
board_a[1][1][2]=1;<br />
board_a[1][2][3]=1;<br />
board_a[1][3][1]=1;<br />
board_a[1][3][2]=1;<br />
board_a[1][3][3]=1;<br />
Serial.println("inited");<br />
}<br />
<br />
inline void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21<br />
//6 7 8 9 10 20 19 18 17 16<br />
//11 12 13 14 15 15 14 13 12 11<br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
// dump(board_a);<br />
custom_eyes(4,board_to_eyes(board_a));<br />
play(board_a,board_b);<br />
delay(50);<br />
// dump(board_b);<br />
custom_eyes(4,board_to_eyes(board_b));<br />
play(board_b,board_a);<br />
delay(50);<br />
<br />
//TODO: add something to react to remote here, e.g. set some cells etc.<br />
if (irrecv.decode(&results)){<br />
irrecv.resume();<br />
} else {<br />
}<br />
}<br />
<br />
</pre><br />
<br />
===== Countdown Timer =====<br />
<br />
This sketch turns Vortex into an eggtimer to be used to keep track of how much time you have left when giving a talk, or before your food is ready. It displays the time left on the eyes, and can be controlled with a IR remote to (re)set the timer. LED colors are adjusted based on the time left, with vortex blinking red when you're out of time. <br />
<pre><br />
#include <Wire.h> <br />
#include <IRremote.h> <br />
#include <FastLED.h> <br />
//IR receiver pin <br />
#define IR_IN (7) <br />
#define I2C_LED_ADDRESS 0b100000 <br />
#define I2C_WRITE 0x00 <br />
// How many leds are in the strip? <br />
#define NUM_LEDS 12 <br />
// Data pin that led data will be written out over <br />
#define DATA_PIN 13 <br />
CRGB leds[NUM_LEDS]; <br />
// how many milliseconds per count (1000=count seconds, 60000 count minutes) <br />
unsigned long MperC=60000; <br />
<br />
unsigned long start; <br />
short countdown; <br />
short displayed; <br />
uint8_t state=0; <br />
unsigned long m_elapsed; <br />
short c_elapsed; <br />
unsigned long long current=0xffffffff; <br />
unsigned long long last=0xffffffff; <br />
<br />
IRrecv irrecv(IR_IN); <br />
decode_results results; <br />
<br />
uint8_t eyes[10]={0,0,0,0,0,0,0,0,0,0}; <br />
<br />
uint8_t left_digit[10][5]={ <br />
{0x04,0x0A,0x0A,0x0A,0x04}, //0 <br />
{0x0E,0x04,0x04,0x0C,0x04}, //1 <br />
{0x0E,0x08,0x04,0x02,0x0C}, //2 <br />
{0x0C,0x02,0x0C,0x02,0x0C}, //3 <br />
{0x02,0x02,0x0E,0x0A,0x0A}, //4 <br />
{0x0E,0x02,0x0C,0x08,0x0E}, //5 <br />
{0x04,0x0A,0x0C,0x08,0x04}, //6 <br />
{0x04,0x04,0x02,0x02,0x0C}, //7 <br />
{0x04,0x0A,0x04,0x0A,0x04}, //8 <br />
{0x04,0x02,0x06,0x0A,0x04} //9 <br />
}; <br />
uint8_t right_digit[10][5]; <br />
uint8_t swap(uint8_t l) { <br />
switch (l&0x1f){ <br />
case 0x0: return 0; <br />
case 0x01: return 0x10; <br />
case 0x02: return 0x08; <br />
case 0x03: return 0x18; <br />
case 0x04: return 0x04; <br />
case 0x05: return 0x14; <br />
case 0x06: return 0x0C; <br />
case 0x07: return 0x1C; <br />
case 0x08: return 0x02; <br />
case 0x09: return 0x12; <br />
case 0x0a: return 0x0A; <br />
case 0x0b: return 0x1A; <br />
case 0x0c: return 0x06; <br />
case 0x0d: return 0x16; <br />
case 0x0e: return 0x0E; <br />
case 0x0f: return 0x1E; <br />
case 0x10: return 0x01; <br />
case 0x11: return 0x11; <br />
case 0x12: return 0x09; <br />
case 0x13: return 0x19; <br />
case 0x14: return 0x05; <br />
case 0x15: return 0x15; <br />
case 0x16: return 0x0D; <br />
case 0x17: return 0x1d; <br />
case 0x18: return 0x03; <br />
case 0x19: return 0x13; <br />
case 0x1a: return 0x0B; <br />
case 0x1b: return 0x1B; <br />
case 0x1c: return 0x07; <br />
case 0x1d: return 0x17; <br />
case 0x1e: return 0x0F; <br />
case 0x1f: return 0x1F; <br />
default: return 0; <br />
} <br />
} <br />
void setup(){ <br />
char i,j; <br />
delay(2000); <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
for (i=0;i<10;i++){ <br />
for (j=0;j<5;j++) { <br />
right_digit[i][j]=swap(left_digit[i][4-j]); <br />
} <br />
} <br />
irrecv.enableIRIn(); <br />
Wire.begin(); // join i2c bus (address optional for master) <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
for (i=0;i<12;i++){ <br />
leds[i]=CRGB::Green; <br />
} <br />
FastLED.show(); <br />
start=millis(); <br />
displayed=100; <br />
countdown=99; <br />
state=0; <br />
}<br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){ <br />
uint8_t index; <br />
// right eye left eye <br />
//1 2 3 4 5 25 24 23 22 21 <br />
//6 7 8 9 10 20 19 18 17 16 <br />
//11 12 13 14 15 15 14 13 12 11 <br />
//16 17 18 19 20 10 9 8 7 6 <br />
//21 22 23 24 25 5 4 3 2 1 <br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4 <br />
Wire.write(0x55); // color 55=custom eyes follow <br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line <br />
Wire.write(color&0x7); //color 1-B,2-G,4-R <br />
<br />
for (index=0;index<10;index++) { <br />
Wire.write(eyeline[index]); <br />
} <br />
Wire.endTransmission(); // stop transmitting <br />
// A9 = some sort of multicolor mode <br />
// AA = one color byte, then 10 bytes defining the 2 eyes. <br />
// AB = some sort of multicolor mode <br />
} <br />
void update_display(short displayed) { <br />
byte h=displayed/10; <br />
byte l=displayed%10; <br />
eyes[0]=left_digit[l][0]; <br />
eyes[1]=left_digit[l][1]; <br />
eyes[2]=left_digit[l][2]; <br />
eyes[3]=left_digit[l][3]; <br />
eyes[4]=left_digit[l][4]; <br />
eyes[5]=right_digit[h][0]; <br />
eyes[6]=right_digit[h][1]; <br />
eyes[7]=right_digit[h][2]; <br />
eyes[8]=right_digit[h][3]; <br />
eyes[9]=right_digit[h][4]; <br />
if (displayed<5) { <br />
custom_eyes(4,eyes); <br />
} else { <br />
custom_eyes(2,eyes); <br />
} <br />
} <br />
<br />
void update_leds(short displayed) { <br />
char i; <br />
if (displayed>5) { <br />
for (i=0;i<12;i++){ <br />
leds[i]=CRGB(0,displayed*2,0); <br />
} <br />
} else if (displayed>0) { <br />
for (i=0;i<12;i++){ <br />
leds[i]=0xff9900; <br />
} <br />
} else if (state==1) { <br />
for (i=0;i<6;i++) { <br />
leds[i]=CRGB::Red; <br />
leds[i+6]=CRGB::Black; <br />
} <br />
state=0; <br />
} else if (state==0) { <br />
for (i=0;i<6;i++) { <br />
leds[i+6]=CRGB::Red; <br />
leds[i]=CRGB::Black; <br />
} <br />
state=1; <br />
} <br />
FastLED.show(); <br />
}<br />
void loop(){ <br />
unsigned long m_elapsed=millis()-start; <br />
// FIXME handle overflow of millis. <br />
short c_elapsed=(m_elapsed/MperC); <br />
if (displayed>0) { <br />
if (countdown-c_elapsed!=displayed) { <br />
displayed=countdown-c_elapsed; <br />
displayed=displayed<0?0:displayed; <br />
update_display(displayed); <br />
} <br />
} <br />
update_leds(displayed); <br />
//TODO check IR commands <br />
if (irrecv.decode(&results)) { <br />
current=results.value; <br />
if (current!=last) { <br />
last=current; // store current key to be able to recognize repeats <br />
current=current&0xfffff7ff; // filter out repeat bit. This is for RC5. <br />
<br />
if (current==0xa) { // 'clear' on my particular remote <br />
countdown=0; <br />
} else if (current==0xf) { // 'memo' <br />
start=millis(); // reference time is now <br />
displayed=99; // restarts timer with current countdown setting <br />
} else if (current==0x0) { // 'digit 0', not all remotes might be this regular <br />
digit(0); <br />
} else if (current==0x1) { <br />
digit(1); <br />
} else if (current==0x2) { <br />
digit(2); <br />
} else if (current==0x3) { <br />
digit(3); <br />
} else if (current==0x4) { <br />
digit(4); <br />
} else if (current==0x5) { <br />
digit(5); <br />
} else if (current==0x6) { <br />
digit(6); <br />
} else if (current==0x7) { <br />
digit(7); <br />
} else if (current==0x8) { <br />
digit(8); <br />
} else if (current==0x9) { <br />
digit(9); <br />
} else if (current==0x0) { // TODO add pause, up/down by 1 <br />
} <br />
} <br />
irrecv.resume(); // Receive the next value <br />
} <br />
<br />
delay(500); <br />
} <br />
void digit(byte d) { <br />
displayed=0; // stops timer updates to display <br />
countdown=(countdown%10)*10+d; <br />
update_display(countdown); <br />
}<br />
<br />
</pre><br />
<br />
==== i2c ====<br />
There might be more stuff connected to the i2c already, I plan to do some tests to find out. Would be nice if e.g. the flash of the mp3 player or some other expansion was already there.<br />
<br />
Regardless of what's there already, Vortex seems to be designed to be extended over i2c, and could be a nice platform to do some experiments/demo's with. <br />
<br />
Here's a simple scanning sketch, looks like only 0x40 has something connected, and that would be the eye-display.<br />
<br />
<pre><br />
#include <Wire.h><br />
<br />
<br />
void setup()<br />
{<br />
Wire.begin();<br />
<br />
Serial.begin(9600);<br />
while (!Serial); // Leonardo: wait for serial monitor<br />
Serial.println("\nI2C Scanner");<br />
}<br />
<br />
<br />
void loop()<br />
{<br />
byte error, address;<br />
int nDevices;<br />
<br />
Serial.println("Scanning...");<br />
<br />
nDevices = 0;<br />
for(address = 0; address < 128; address++ )<br />
{<br />
// The i2c_scanner uses the return value of<br />
// the Write.endTransmisstion to see if<br />
// a device did acknowledge to the address.<br />
Wire.beginTransmission(address);<br />
error = Wire.endTransmission();<br />
<br />
if (error == 0)<br />
{<br />
Serial.print("I2C device found at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
<br />
nDevices++;<br />
}<br />
else if (error==4)<br />
{<br />
Serial.print("Unknown error at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
} <br />
}<br />
if (nDevices == 0)<br />
Serial.println("No I2C devices found\n");<br />
else<br />
Serial.println("done\n");<br />
<br />
delay(5000); // wait 5 seconds for next scan<br />
}<br />
</pre><br />
<br />
====Bluetooth====<br />
It seems the Bluetooth functionality is the same as for the "Bluno" and thus the information at https://www.dfrobot.com/wiki/index.php/Bluno_SKU:DFR0267#Wireless_Programming_via_BLE applies.<br />
<br />
The AT commands seem to work, up to a point, but looks like it is a somewhat variant version of the firmware as AT+VERSION command doesn't work. Updating program seem not available for Linux.<br />
<br />
Booting the Vortex with the 'boot' button (near UL7, Vortex bottom-right-back RGB led.), two leds blink, and linux detects the Vortex over USB as:<br />
[230656.359259] usb 1-1.1: new full-speed USB device number 103 using xhci_hcd<br />
[230656.488502] usb 1-1.1: New USB device found, idVendor=2341, idProduct=0043<br />
[230656.488508] usb 1-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[230656.488511] usb 1-1.1: Product: DFRobot Boot CDC<br />
[230656.488513] usb 1-1.1: Manufacturer: DFRobot BLUno Boot<br />
[230656.490491] cdc_acm 1-1.1:1.0: ttyACM0: USB ACM device<br />
<br />
Similarly the little BLE dongle comes up as:<br />
[232626.931574] usb 1-1.3: new full-speed USB device number 108 using xhci_hcd<br />
[232627.254357] usb 1-1.3: New USB device found, idVendor=2341, idProduct=0043<br />
[232627.254362] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[232627.265758] cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device <br />
<br />
If Vortex pairs with the dongle, it effectiely gains an additional, wireless, serial link to a different port on the host. It's connected to the same Serial port on the internal Arduino. Make sure to adjust speed setting to what is used in the currently running sketch, the default 115200 won't work if Serial.begin(9600) is already called..<br />
<br />
<br />
Using AT commands it should be possible to set up the link connect to other Vortex robots too.<br />
<br />
Need to do some experiments to see if it is possible to connect to a generic Bluetooth 4.x dongle too.<br />
<br />
====Expansion====<br />
[[file:Vortex expansion holes drawing.svg|right|frame|hole arangement seen from top, vortex facing left or right]]<br />
There are 2 sets of 4 positions for screws/bolts at the top of the vortex, around the battery compartment, obviously to be used to put an expansion unit on top of Vortex, which could be linked to the i2c bus.<br />
<br />
=====Dimensions=====<br />
<br />
The bigger set of holes are 65 mm apart from left to right and 55 from front to back (center to center). These seem to be meant for 3 mm machine screws<br />
<br />
The smaller set of holes are 2.5" apart from left to right and 2.75" from front to back (center to center). These seem to be meant for small screws biting into the plastic.<br />
<br />
===== The 4 pin expansion port=====<br />
Q: Does Vortex support sensor module expansion?<br />
A: Vortex has a “Maker mode” where you can connect sensors via its IIC/TWI interface.<br />
<br />
It would seem the only logical place for this is the 4 pin connector in the back. Looking at the connector from the back of Vortex:<br />
____<br />
|1234|<br />
-__-<br />
<br />
These pins seem to be connected to the rainbow cable coming up from the main PCB as follows:<br />
<br />
{| class="wikitable"<br />
|+Vortex expansion port<br />
|-<br />
!Pin!!Function!!Color<br />
|-<br />
|1||SDA?||~15kOhm to Blue<br />
|-<br />
|2||SCL?||Blue<br />
|-<br />
|3||Gnd?||Black<br />
|-<br />
|4||Vcc?||Red<br />
|-<br />
|}<br />
<br />
It would make sense for Red to be 5V, Black to be Gnd, and the other two SCL and SDA of the i2c bus. This needs some further testing, preferably with a known good i2c device connected to the expansion port. The ~15kOhm between what I suspect to be SCL and SDA would then be two ~7.5kOhm to some common 5 V point that is buffered from the 'Red' cable.</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Vortex&diff=17586Luteijn/Vortex2018-02-12T06:01:17Z<p>Luteijn: /* The 4 pin expansion port */</p>
<hr />
<div>===Project "Vortex":===<br />
{{Project<br />
|Name=Luteijn/Vortex<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=Vortex_1.jpg<br />
}}<br />
<br />
I've got four DFRobot 'Vortex' units. Basic programming via the official apps is possible, but rather limited. The WhenDo app is only available on iPad. Luckily, the internal Arduino clone can be directly programmed too. Unfortunately, not all the specs seem to be available, but there are some examples to work with, although they have a quite a bit of 'magic numbers' in them. These examples might disappear, so duplicating them here, with my own notes as available. The examples-coding is mostly done by "Andy Zhou <Andy.zhou@dfrobot.com>", although I did change some of the things to figure out what they do.<br />
<br />
http://wiki.dfrobot.com.cn/index.php?title=(SKU:ROB0116)_Vortex%E5%8F%AF%E7%BC%96%E7%A8%8B%E6%9C%BA%E5%99%A8%E4%BA%BA#.E6.A0.B7.E4.BE.8B.E4.BB.A3.E7.A0.81 <br />
<br />
https://www.dfrobot.com/wiki/index.php/Vortex_Arduino_Coding_Tutorial_V1.0#Introduction<br />
<br />
(Chinese page seems to have a bit better documentation, although English page might be easier to read and it is interesting to compare the differences)<br />
<br />
http://wiki.dfrobot.com.cn/images/1/10/%E4%B8%BB%E6%9D%BF.png<br />
<br />
==== What's connected to the pins ====<br />
{| class="wikitable"<br />
|+ Connection overview<br />
|-<br />
|0<br />
|Serial RX (input)<br />
|-<br />
|1<br />
|Serial TX (output)<br />
|-<br />
|2<br />
|Encoder Wheel (External Interrupt 0) (input)<br />
|-<br />
|3<br />
|Encoder Wheel (External Interrupt 1) (input)<br />
|-<br />
|4<br />
|Unknown, seems to be pulled high when used as input. Found it too hard to trace on the board.<br />
|-<br />
|5<br />
|Motor 0 Speed (PWM) (output)<br />
|-<br />
|6<br />
|Motor 1 Speed (PWM) (output)<br />
|-<br />
|7<br />
|IR decoder/detector (input)<br />
|-<br />
|8<br />
|IR led Left (output)<br />
|-<br />
|9<br />
|Motor 0 direction (H=forwards,L=reverse) (output)<br />
|-<br />
|10<br />
|Motor 1 direction (output)<br />
|-<br />
|11<br />
|MP3-player TX (output)<br />
|-<br />
|12<br />
|IR led Right (output)<br />
|-<br />
|13<br />
|GRB LED chain around body (output)<br />
|-<br />
|A0<br />
|grayscale 4 'D' (input)<br />
|-<br />
|A1<br />
|grayscale 3 'C' (input)<br />
|-<br />
|A2<br />
|grayscale 2 'B' (input)<br />
|-<br />
|A3<br />
|grayscale 1 'A' (input)<br />
|-<br />
|A4<br />
|SDA (i2c)<br />
|-<br />
|A5<br />
|SCL (i2c)<br />
|-<br />
|A6<br />
|grayscale 5 'E' (input)<br />
|-<br />
|A7<br />
|grayscale 6 'F' (input)<br />
|-<br />
|}<br />
<br />
i2c bus + Vcc & Gnd should be available at 4 pin rear expansion port too - need to find a suitable connector for it..<br />
<br />
http://www.hobbytronics.co.uk/arduino-atmega328-pinout Useful diagrams to map pins to package<br />
<br />
==== Dumping original firmware ====<br />
Although you can/should be able to reflash Vortex with the App, might as well make a backup of the orignal:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:r:"Vortex.hex":i<br />
This can be uploaded again with:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:w:"Vortex.hex":i<br />
(The app might also be initializing other things, but this seems to work)<br />
<br />
Your own sketches can be uploaded with <br />
arduino --upload MySketch.ino<br />
But using avrdude to upload the .hex file is a bit quicker.<br />
<br />
==== Motor Control ====<br />
<br />
Simple 'enable' and 'direction' pins for left and right wheels. Enable can be PWM'd or just full on.<br />
This example revs up the engines with PWM.<br />
<pre><br />
int E1 = 5; <br />
int M1 = 9; <br />
int E2 = 6; <br />
int M2 = 10; <br />
<br />
void setup() <br />
{ <br />
pinMode(M1, OUTPUT); // directional controls, High is forward, Low is backward <br />
pinMode(M2, OUTPUT); <br />
} <br />
<br />
void loop() <br />
{ <br />
int value; <br />
for(value = 0 ; value <= 255; value+=5) //foreward <br />
{ <br />
digitalWrite(M1,HIGH); <br />
digitalWrite(M2, HIGH); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
<br />
for(value = 0 ; value <= 255; value+=5) //backward <br />
{ <br />
digitalWrite(M1, LOW); <br />
digitalWrite(M2, LOW); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
} <br />
</pre><br />
<br />
====Encoders====<br />
The two external interrupts are linked to encoders that track wheel movement, so it is possible to detect the Vortext is being pushed/pulled/turned, although I don't think these things indicate which direction the robot is pushed in...<br />
<br />
<pre><br />
#define pinInputLeft 0 // this is the interrupt, not the pin number, D2<br />
#define pinInputRight 1 // external int 1, D3<br />
long leftPul,rightPul;<br />
<br />
void leftCallBack(){<br />
leftPul++;<br />
}<br />
<br />
void rightCallBack(){<br />
rightPul++;<br />
}<br />
<br />
void initDdevice(){<br />
pinMode(5,OUTPUT);<br />
pinMode(6,OUTPUT);<br />
pinMode(9,OUTPUT);<br />
pinMode(10,OUTPUT);<br />
noInterrupts();<br />
attachInterrupt(pinInputLeft,leftCallBack,CHANGE);<br />
attachInterrupt(pinInputRight,rightCallBack,CHANGE);<br />
interrupts();<br />
}<br />
<br />
void motorDebug(){<br />
digitalWrite(5,HIGH);<br />
digitalWrite(6,HIGH);<br />
digitalWrite(9,HIGH);<br />
digitalWrite(10,HIGH);<br />
}<br />
<br />
void printPul(){<br />
Serial.print(leftPul);<br />
Serial.print(" ");<br />
Serial.println(rightPul);<br />
leftPul = 0;<br />
rightPul = 0;<br />
}<br />
<br />
void setup() {<br />
initDdevice();<br />
Serial.begin(9600);<br />
motorDebug();<br />
}<br />
<br />
void loop() {<br />
printPul();<br />
delay(500);<br />
}<br />
</pre><br />
====Analogue Inputs====<br />
six 'grayscale' detectors are placed around the bottom of the Vortex, to be used to track a line, maybe read simple codes from the floor.<br />
<pre> <br />
2 (a2) 3 (a1) <br />
1 (a3) 4 (a0)<br />
<br />
<br />
<br />
<br />
5 (a6) 6 (a7)<br />
<br />
</pre><br />
This is the example sketch to dump the values read by the 6 little eyes.<br />
<pre><br />
void setup(void){<br />
Serial.begin(9600); <br />
}<br />
<br />
int analogBuf[6] = {'\0'};<br />
<br />
void loop(void){<br />
analogBuf[0] = analogRead(3);<br />
analogBuf[1] = analogRead(2);<br />
analogBuf[2] = analogRead(1);<br />
analogBuf[3] = analogRead(0);<br />
analogBuf[4] = analogRead(6);<br />
analogBuf[5] = analogRead(7);<br />
for(int i=0;i<6;i++){<br />
Serial.print(i+1);<br />
Serial.print(": ");<br />
Serial.print(analogBuf[i]);<br />
Serial.print(" ");<br />
}<br />
Serial.println();<br />
delay(500);<br />
}<br />
</pre><br />
<br />
Wonder what's connected to A4 and A5, if anything? These are used for the i2c bus! See below under the [[Luteijn/Vortex#Eyes]] section<br />
<br />
====LEDs====<br />
12 RGB (GRB!) leds can be addressed. Example from website causes Red and Green to be swapped. initialize library differently:<br />
<pre><br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
</pre><br />
IR example below uses some of the LED functionality.<br />
<br />
====IR obstacle detector====<br />
Seems to be somewhat flaky. Needs a bit more work to figure out. Also, the IR sensor is useful to read IR remote controllers or beacons. This receiver seems to work at 38kHz, but probabbly also reacts to 36-40kHz. Remote controllers often work at 36kHz. Note that 2x 8µs delay doesn't give you 38kHz, but the digitalWrite itself also induces quite some delay (3-6µs), so seems this is bringing the total period up to around the 26.something µs we'd expect.<br />
<br />
<br />
<pre><br />
#define NUM_LEDS 12 <br />
<br />
// Data pin that led data will be written out over <br />
#define DATA_PIN 13 <br />
CRGB leds[NUM_LEDS]; <br />
<br />
#define IR_IN 7//IR receiver pin <br />
#define L_IR 8 //left ir transmitter pin <br />
#define R_IR 12 //right ir transmitter pin <br />
<br />
int count; <br />
<br />
void leftSend38KHZ(void){//left ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(L_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(L_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
void rightSend38KHZ(void){//right ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(R_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(R_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
<br />
void pcint0Init(void){//init the interrupt <br />
PCICR |= 1 << PCIE2; <br />
PCMSK2 |= 1 << PCINT23; //pin D7 is int23 <br />
} <br />
<br />
ISR(PCINT2_vect){//IR decoder interrupt <br />
count++; <br />
} <br />
<br />
void obstacleAvoidance(void){ <br />
char i; <br />
count=0; <br />
leds[5] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){ //left transmitter sends 20 pulses <br />
leftSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[5] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Left"); <br />
delay(100); <br />
} <br />
count=0; <br />
leds[3] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){//right transmitter sends 20 pulses <br />
rightSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[3] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Right"); <br />
delay(100); <br />
} <br />
delay(600); <br />
} <br />
<br />
void setup(void){ <br />
delay(2000); <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
pinMode(L_IR,OUTPUT);//init the left transmitter pin <br />
pinMode(R_IR,OUTPUT);//init the right transmitter pin <br />
pinMode(IR_IN,INPUT);//init the ir receiver pin <br />
Serial.begin(9600); <br />
leds[4] = CRGB::Blue; <br />
FastLED.show(); <br />
delay(2000); <br />
leds[4] = CRGB::Purple; <br />
FastLED.show(); <br />
noInterrupts(); <br />
pcint0Init(); <br />
interrupts(); //enable the interrupt <br />
} <br />
<br />
void loop(void){ <br />
obstacleAvoidance(); <br />
}<br />
</pre><br />
<br />
==== IRreceiver ====<br />
Since DFrobot also have a 'loose' IR-receiver module in their program, decided to just see if the example sketches for those would transfer, and they do.<br />
https://www.dfrobot.com/product-366.html - A remote similar to the one pictured on the DFRobot product page actually came with one of my RTLSDR sticks, and seems to work well enough, although the range isn't great, couple of meters. With a 'proper' remote control, e.g. from a TV, the range is fine, 10m at least when pointing at unit from across the house. Might be less if using reflections to try to control a roving robot.<br />
<br />
So, using the IRremote library you can read values from a remote controller:<br />
<pre><br />
#include "IRremote.h"<br />
#define IR_IN 7//IR receiver pin<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
<br />
void setup(void){<br />
Serial.begin(9600); <br />
irrecv.enableIRIn(); // Start the receiver<br />
} <br />
<br />
void loop(void){ <br />
if (irrecv.decode(&results)) { <br />
Serial.println(results.value, HEX); <br />
irrecv.resume(); // Receive the next value<br />
} <br />
} <br />
</pre><br />
This was already useful to read codes from the remote of the AV-receiver we have at home and then play these back via a universal remote app on the smartphone that was missing some buttons. For some reason I couldn't find the 'learn' function in the app, but could enter the codes as read by this sketch easily.<br />
<br />
The receiver is mounted on the front, bottom. So not right between the eyes, although a spot seems to be prepared for it there too.<br />
<br />
The tables at [[Luteijn/IRRemotes]] might be useful when creating a sketch that is to be controlled over IR.<br />
<br />
Here's an example of a simple driving sketch controlled via my RTeL-Cheapo remote.<br />
<pre><br />
#include "IRremote.h"<br />
<br />
#define IR_IN (7)//IR receiver pin<br />
#define E1 (5)<br />
#define E2 (6)<br />
#define M1 (9)<br />
#define M2 (10)<br />
<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
// MSB is direction, 0xff full forward, 0x00 full backwards<br />
uint8_t Left=128; // 0x80 : forward, speed 0<br />
uint8_t Right=128; // 0x80 : forward, speed 0<br />
<br />
void setup(void){<br />
delay(2000); // grace period<br />
Serial.begin(9600);<br />
pinMode(M1, OUTPUT);<br />
pinMode(M2, OUTPUT);<br />
Engine(Left,Right);<br />
irrecv.enableIRIn(); // Start the receiver<br />
}<br />
<br />
void Engine(uint8_t Left, uint8_t Right) {<br />
uint8_t D1=Left&0x80;<br />
uint8_t D2=Right&0x80;<br />
uint8_t S1=D1?Left&0x7f:0x7f-(Left&0x7f);<br />
uint8_t S2=D2?Right&0x7f:0x7f-(Right&0x7f);<br />
Serial.print(Left,HEX);<br />
Serial.print("<-L R->");<br />
Serial.println(Right,HEX);<br />
digitalWrite(M1,D1);<br />
analogWrite(E1,S1<<1);<br />
digitalWrite(M2,D2);<br />
analogWrite(E2,S2<<1);<br />
}<br />
uint32_t last;<br />
void loop(void){<br />
uint32_t code;<br />
if (code=irrecv.decode(&results)) {<br />
code=results.value;<br />
Serial.print("Got IR:");<br />
Serial.println(code,HEX);<br />
<br />
if (code==0xFFFFFFFF) {code=last;}; // repeated press<br />
<br />
if (code==0xFFB24D) { // power = full stop<br />
Left=128;<br />
Right=128;<br />
} else if (code==0xFF02FD) { // full screen both go towards full stop<br />
if (Left<128) Left++; else Left--;<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFFA05F) { // ch+ increase both towards full ahead<br />
if (Left<255) Left++;<br />
if (Right<255) Right++;<br />
} else if (code==0xFF40BF) { // ch- decrease both towards full reverse<br />
if (Left>0) Left--;<br />
if (Right>0) Right--;<br />
} else if (code==0xFF50AF) { // vol- left towards full stop<br />
if (Left<128) Left++; else Left--;<br />
} else if (code==0xFF32CD) { // record increase left towards full ahead<br />
if (Left<255) Left++;<br />
} else if (code==0xFF48B7) { // 0 decrease left towards full reverse<br />
if (Left>0) Left--;<br />
} else if (code==0xFF7887) { // vol+ right towards full stop<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFF30CF) { // time shift increase right towards full ahead<br />
if (Right<255) Right++;<br />
} else if (code==0xFF38C7) { // recall decrease right towards full reverse<br />
if (Right>0) Right--;<br />
} else {<br />
// ignore unknown codes<br />
}<br />
last=code;<br />
irrecv.resume(); // Receive the next value<br />
Engine(Left,Right);<br />
}<br />
}<br />
</pre><br />
It would be better to use a stronger remote for this as it's hard to successfully control the robot when it's facing away from you. Also, the speed ramp up/down is a bit slow, and should probably take bigger steps than one at a time. <br />
<br />
Left as an exercise to the reader: add controls to spin in place left/right, using interrupts of wheel encoders to rotate 45/90 etc. degrees, boundary detection with analogue sensors, object detection with IR (careful not to make control harder). Switch to a bluetooth remote controller once figured out the possibilities of bluetooth better?<br />
<br />
====MP3-player====<br />
There is an MP3.player integrated in the robot. It can be controlled by sending more or less magic commands over software Serial via pin 11. Pin 2 might be connected to the player too if the example I found can be believed, but my tests so far never had anything received there, or on pin 4. Would be nice to get an 'end of song reached' or to get things like version information out. Anyway, Pin 2 is connected to one of the wheel encoders, so not likely to be the RX. It might also be co-connected to one of the relatively harmless outputs, like to the ir emitter? Will need to trace the board to find out, but initial quick look didn't immediately show anything.<br />
<br />
The example found on the DFRobot site:<br />
<pre><br />
#define MP3_VOLUME 0x10<br />
#define TX 11<br />
#define RX 4 // ?? was 2 in example I found, but that is unlikely<br />
#include <SoftwareSerial.h><br />
<br />
SoftwareSerial mySerial(RX, TX);// RX, TX<br />
void setup()<br />
{<br />
delay(1000); <br />
mp3Init();<br />
mp3setVolume(30);//0~30<br />
}<br />
void loop()<br />
{<br />
mp3player(1);<br />
delay(2000);<br />
mp3stop();<br />
delay(1000);<br />
}<br />
void mp3Init()<br />
{<br />
mySerial.begin (9600);<br />
}<br />
void mp3setVolume(byte vol)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
void mp3player(byte data)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
<br />
void mp3stop()<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x16, 0x00, 0x00, 0x00, 0xef};<br />
mySerial.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
The files to play can be put on the Vortex via usb, seems the little switch next to the micro-usb socket switches this between the mp3-players mass-storage and the arduino usb-serial interface. The number passed to the player is just the index into the (FAT?) table of stored songs. So, be careful of the order these are uploaded to the memory in. <br />
<br />
https://www.dfrobot.com/product-1121.html , documented at https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299 is a mini-MP3 player from DFRobot. It is controllable over serial, and might be the same one as used in the Vortex. Would be good to figure out if the 'busy' signal and/or the serial TX are connected back to the arduino somewhere in that case. Player could also be a variant but the magic numbers more or less match (although the stop command 0x16 is not in the table. <br />
<br />
The MP3-chip inside Vortex has Part# YX6100-24SS. Seems to be part of a family of serial MP3-players made by "广州悦欣电子科技有限公司" --> "Guangzhou Yue Xin Electronic Technology Co., Ltd." - website http://www.yx080.com/mp3xinpian/53-15.html has a download link on this yx6100 page but it seems to just refer us to the 5200 application Manual V1.8, which appears to be mostly compatible. The command table for the chip does include a 0x16 stop command. <br />
<br />
Also the datasheet mentions the chip can play both MP3 and WAV. YX5300-24SS is a similar part, datasheet at https://xp-dev.com/trac/arduino_antonio/export/280/arduino_antonio/trunk/EnterpriseBase/Serial%20MP3%20Player/About%20the%20Chip%20-%20YX5300/YX5300-24SS%20Datasheet%20V1.0.pdf The command table for that chip does include a 0x16 stop command.<br />
<br />
Documentation for the DFRobot miniplayer mentions the instruction format to be:<br />
{| class="wikitable"<br />
|+ Format: $S VER Len CMD Feedback para1 para2 checksum $O<br />
|-<br />
| $S<br />
| Start byte 0x7e<br />
| Each command begins with 0x7e ('~')<br />
|-<br />
| VER<br />
| Version information<br />
| Version seems to be 0xff from the example<br />
|-<br />
| Len<br />
| the number of bytes <br />
| Start, End and Checksum are not counted,<br />
|-<br />
| CMD<br />
| Commands<br />
| See command table.<br />
|-<br />
| Feedback<br />
| Command feedback<br />
| 1: feedback, 0: no feedback ; need to test what this does, might mean 'echo'<br />
|-<br />
| Para1<br />
| Parameter 1<br />
| Query high data byte<br />
|-<br />
| Para2<br />
| Parameter 2<br />
| Query low data byte<br />
|-<br />
| checksum<br />
| Checksum<br />
| accumulation and verification, doesn't include start byte. Seems to be 2 bytes from the example given in the docs, but Vortex seems to just not need it put in. example given is 7e ff 06 09 00 00 04 ff dd ef ; The 5200 Datasheet uses the exact same example command, and explains that you should add all the bytes, and then subtract those from 0 to get the checksum. The 5300 Datasheet also mentions "另外用户也可以直接忽视校验,参考我们的5.3.4 章节说明。" so looks like the checksum is optional if you don't care too much about corruption of the occasional command.<br />
|-<br />
| $O<br />
| End byte<br />
| End of command is signaled with 0xEF<br />
|}<br />
<br />
{| class="wikitable"<br />
|+Command table WIP to copy this over<br />
|-<br />
!CMD<br />
!Function Description<br />
!Parameters (16 bit)<br />
!compared with YX5300 info<br />
|-<br />
|0x01<br />
|Next<br />
|<br />
|-<br />
|0x02<br />
|Previous<br />
|<br />
|-<br />
|0x03<br />
|Specific track<br />
|0-2999 but example is only using low byte. also track 0 doesn't seem to do anything?<br />
|Indicates 1-255 are the valid tracks only.<br />
|-<br />
|0x04<br />
|Volume up<br />
|<br />
|-<br />
|0x05<br />
|Volume down<br />
|<br />
|-<br />
|0x06<br />
|Set Volume<br />
|0-30 (10 is already quite loud!)<br />
|-<br />
|0x07<br />
|Specify EQ(0/1/2/3/4/5/)<br />
|Normal/Pop/Rock/Jazz/Classic/Base.<br />
|Reserved in the 5300's command list.<br />
|-<br />
|0x08<br />
|Specify playback mode (0/1/2/3)<br />
|Repeat/folder repeat/single repeat/random<br />
|See 3.4.3; this explains the argument is the song to play in a loop - may have been changed for 6100<br />
|-<br />
|0x09<br />
|Select source (0/1/2/3/4)<br />
|U/TF/AUX/SLEEP/FLASH<br />
|See 3.4.4; specifies 1 as U, 2 as TF, 4 as PC, 5 FLASH and 6 SLEEP<br />
|-<br />
|0x0a<br />
|Enter into standby - low power loss<br />
|<br />
|Sleep - Low power consumption 10MA (obviously mA or µA, not MA, is meant)<br />
|-<br />
|0x0b<br />
|Normal working<br />
|<br />
|Wake up from sleep<br />
|-<br />
|0x0c<br />
|reset module<br />
|<br />
|chip reset<br />
|-<br />
|0x0d<br />
|Playback<br />
|<br />
|Play - seems to be contrast to suspend/pause<br />
|-<br />
|0x0e<br />
|Pause<br />
|<br />
|Suspended<br />
|-<br />
|0x0f<br />
|Specify folder to playback<br />
|1~10(need to set by user)<br />
|see 3.4.5 - DH: represents the name of the folder, the default support for 99 files, 01 - 99 named DL: represents the track, the default maximum of 255 songs, that is, 0x01 ~ 0xFF<br />
|-<br />
|0x10<br />
|Volume adjust set<br />
|DH=1:open volume adjust DL: set volume gain 0~31<br />
|not present in command table <br />
|-<br />
|0x11<br />
|Repeat play<br />
|1:start repeat play 0: stop play<br />
|not present in command table<br />
|-<br />
|0x16<br />
|not in the table<br />
|example mentions it as a stop command<br />
|Stop<br />
|-<br />
|0x17<br />
|not in the table<br />
|<br />
|FLASH only, see 3.4.7 (but is in section 3.4.6), select folder to loop.<br />
|-<br />
|0x18<br />
|not in the table<br />
|<br />
|Reserved<br />
|-<br />
|0x19<br />
|not in the table<br />
|<br />
|See 3.4.8 (but is in section 3.4.7), select loop current track mode<br />
|-<br />
|0x21<br />
|not in the table<br />
|<br />
|See 3.4.9 (3.4.8), set DAC to High-Z mode (1) or on (0) so you can use the amp for something else.<br />
|-<br />
|0x22<br />
|not in the table<br />
|<br />
|See 3.4.10 (section 3.4.9 doesn't exist), Set volume and song to play in one command (H:volume L:track)<br />
|-<br />
|0x3C<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3D<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3E<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3F<br />
|Send initialization parameters<br />
|0-0x0F (each bit represent one device of the low-four bits)<br />
|Seems to be reporting which storage devices are currently online (see section 3.5.1) <br />
|-<br />
|0x40<br />
|Returns an error, request retransmission<br />
|<br />
|Probably this is a response to a command meaning Error Encountered.<br />
|-<br />
|0x41<br />
|Answer<br />
|<br />
|Probably this is a response to a query and just means Accepted.<br />
|-<br />
|0x42<br />
|Query Status<br />
|<br />
|See 3.4.10 (actually 3.5.2) - explains that this will return which storage is used and if it's playing, stopped or paused. The storage could also be 'SLEEP' to indicate sleeping?<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
<br />
|}<br />
<br />
Code to calculate the checksum from the Library supporting the miniplayer:<br />
<pre><br />
uint16_t DFRobotDFPlayerMini::calculateCheckSum(uint8_t *buffer){<br />
uint16_t sum = 0;<br />
for (int i=Stack_Version; i<Stack_CheckSum; i++) {<br />
sum += buffer[i];<br />
}<br />
return -sum;<br />
}<br />
</pre><br />
but this doesn't seem to be needed.<br />
<br />
====Eyes====<br />
The 'Eyes' are connected over i2c. The daughterboard they are on features an STM8S103K3 (see http://www.st.com/en/microcontrollers/stm8s103-105.html ) and 2x HC595 (shift registers).<br />
Besides 35 predefined eye patterns, it is also possible to upload your own eye patterns.<br />
<br />
The default eyes can be set as follows:<br />
<pre><br />
#include <Wire.h><br />
#define I2C_LED_ADDRESS 0b1100000<br />
#define I2C_WRITE 0x00<br />
<br />
uint8_t serial=0;<br />
<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
defined_eyes(6,1); <br />
delay(2000);<br />
}<br />
void loop(){<br />
defined_eyes(1,serial); <br />
serial++; <br />
if(serial>=35) serial=0; <br />
delay(250); <br />
}<br />
<br />
<br />
void defined_eyes(uint8_t color,uint8_t serial) {<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4 <br />
// (I suppose they mean 0x40, although eyes seem to also respond on 0xC0 which is what is defined above, and 0x20)<br />
Wire.write(color&0x07); // color bits: 1 blue, 2 green, 4 red <br />
Wire.write(serial); //preset eyes 0~34<br />
Wire.endTransmission(); // stop transmitting <br />
}<br />
<br />
</pre><br />
<pre><br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21 <br />
//6 7 8 9 10 20 19 18 17 16 <br />
//11 12 13 14 15 15 14 13 12 11 <br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1 <br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4<br />
Wire.write(0x55); // color 55=custom eyes follow <br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
// other values cause eyes to light up in multiple colors, needs more work<br />
<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R of foreground<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
} <br />
Wire.endTransmission(); // stop transmitting <br />
<br />
<br />
void example_eyes() {<br />
/* left eye of robot (right for onlooker) */<br />
Wire.write(0x1f); // bottom row, all bits lit<br />
Wire.write(0x1e); // 10 9 8 7 on, 6 off<br />
Wire.write(0x1c);<br />
Wire.write(0x18);<br />
Wire.write(0x10);<br />
<br />
/* right eye of robot (left for onlooker) */<br />
Wire.write(0x1f); // top row all 5 bits lit<br />
Wire.write(0x0f); // leds 6,7,8,9 on 10 off<br />
Wire.write(0x07); // 11,12,13 on, 14,15 off <br />
Wire.write(0x03); <br />
Wire.write(0x01); <br />
} <br />
</pre><br />
<br />
=====KITT-scanner / Cylon=====<br />
This animates the eyes and adds some simple sound effects - currently the eye warble is not synchronized to the eye movement. The IR remote is used to trigger the well-known 'By your command'. For a KITT scanner may want to change to the proper woosh for a KITT, and replace the command sample with something like 'yes, Michael' :)<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <SoftwareSerial.h><br />
#include <IRremote.h><br />
<br />
#define MTX (11)<br />
#define MRX (4)<br />
<br />
// Song number for "By your command"-sample<br />
#define BYC (30)<br />
// Song number for Warble<br />
#define Warble (31)<br />
<br />
#define IR_IN (7)<br />
<br />
#define I2C_LED_ADDRESS 0x80<br />
bool left=1;<br />
decode_results results;<br />
SoftwareSerial mp3(MRX,MTX);<br />
IRrecv irrecv(IR_IN);<br />
short command=0;<br />
<br />
uint8_t eyes[10]={0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00};<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
mp3Init();<br />
irrecv.enableIRIn(); // Start the receiver<br />
delay(2000);<br />
delay(100);<br />
mp3setVolume(10);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(2); // repeat once<br />
delay(50);<br />
mp3player(BYC);<br />
delay(3000);<br />
mp3setVolume(8);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
delay(50);<br />
}<br />
void lscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]<<1);<br />
eyes[7]=(uint8_t)(eyes[7]<<1);<br />
}<br />
<br />
void rscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]>>1);<br />
eyes[7]=(uint8_t)(eyes[7]>>1);<br />
}<br />
<br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
<br />
// 1 2 4 8 10 '20' 10 8 4 2 1<br />
if (left) {lscroll_eyes(eyes);}<br />
else if (!left) {rscroll_eyes(eyes);}<br />
<br />
custom_eyes(4,eyes);<br />
<br />
if (eyes[7]==0x1) {left=1;}<br />
else if (eyes[2]==0x1) {left=1;}<br />
else if (left && eyes[2]>=0x10) {eyes[2]=0;eyes[7]=0x20;left=0;}<br />
else if (left && eyes[7]>=0x10) {eyes[7]=0;eyes[2]=0x20;left=0;}<br />
if (irrecv.decode(&results)){<br />
if (command) {<br />
command=0;<br />
} else{<br />
command=34; // adjust to match sample length<br />
mp3player(BYC);<br />
delay(50);<br />
}<br />
irrecv.resume();<br />
} else {<br />
if (command) {<br />
command--;<br />
if (!command){<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
} else {<br />
delay(80);<br />
}<br />
} else {<br />
delay(80);<br />
}<br />
}<br />
}<br />
<br />
void mp3Init()<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x0C, 0x00, 0x00, 0x00, 0xef};<br />
mp3.begin(9600);<br />
delay(100);<br />
mp3.write(buffer, 8);<br />
delay(100);<br />
}<br />
void mp3setVolume(byte vol)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3player(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3repeat(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x08, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
===== Conway's Game of Life =====<br />
2 5x5 fields linked as a torus of 10x5. Change mapping of cells in the edge to make e.g. a front and a back side, or change to 8x8 and add 4 more faces and a bit more mapping to create an LED cube of 6 faces. Simple animation on the 2 eye matrices. This version doesn't actually do something with the remote control, but it was easy to add things like reset board, change color, introduce disturbance etc.<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <IRremote.h><br />
<br />
#define IR_IN (7)<br />
#define I2C_LED_ADDRESS 0x80<br />
<br />
// 5x5 matrix eye<br />
#define CX (5)<br />
#define CY (5)<br />
// edge of 1 cell on each end<br />
#define CXM (7)<br />
#define CYM (7)<br />
<br />
#define F (2) // 2 faces have I.<br />
<br />
decode_results results;<br />
IRrecv irrecv(IR_IN);<br />
<br />
uint8_t eyes[10];<br />
<br />
uint8_t board_a[F][CYM][CXM];<br />
uint8_t board_b[F][CYM][CXM];<br />
<br />
<br />
inline void clear(uint8_t board[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
for (k=0;k<F;k++) {<br />
for (j=0;j<CYM;j++) {<br />
for (i=0;i<CXM;i++) {<br />
board[k][j][i]=0;<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t neighbors(uint8_t s[][CYM][CXM], uint8_t k,uint8_t j,uint8_t i) {<br />
return s[k][j-1][i-1]+s[k][j-1][i]+s[k][j-1][i+1]+s[k][j][i-1]+s[k][j][i+1]+s[k][j+1][i-1]+s[k][j+1][i]+s[k][j+1][i+1];<br />
}<br />
<br />
inline void play(uint8_t s[][CYM][CXM], uint8_t d[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
// TODO: load edges from right faces hardcoded for 5x5 now<br />
for (i=1;i<=5;i++) {<br />
s[0][0][i]=s[0][5][i];<br />
s[0][6][i]=s[0][1][i];<br />
<br />
s[0][i][0]=s[1][i][5];<br />
s[0][i][6]=s[1][i][1];<br />
<br />
s[1][0][i]=s[1][5][i];<br />
s[1][6][i]=s[1][1][i];<br />
<br />
s[1][i][0]=s[0][i][5];<br />
s[1][i][6]=s[0][i][1];<br />
<br />
}<br />
// corners<br />
s[0][0][0]=s[1][5][5];<br />
s[0][6][0]=s[1][1][5];<br />
<br />
s[0][0][6]=s[1][5][1];<br />
s[0][6][6]=s[1][1][1];<br />
<br />
s[1][0][0]=s[0][5][5];<br />
s[1][6][0]=s[0][1][5];<br />
<br />
s[1][0][6]=s[0][5][1];<br />
s[1][6][6]=s[0][1][1];<br />
<br />
<br />
// update interior cells<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
switch (neighbors(s,k,j,i)) {<br />
case 2: d[k][j][i]=s[k][j][i]; // remain<br />
break;<br />
case 3: d[k][j][i]=1; // birth<br />
break;<br />
default: d[k][j][i]=0; // death<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t convert_l(uint8_t l[CXM]) {<br />
return (l[5]|l[4]<<1|l[3]<<2|l[2]<<3|l[1]<<4);<br />
}<br />
inline uint8_t convert_r(uint8_t l[CXM]) {<br />
return (l[1]|l[2]<<1|l[3]<<2|l[4]<<3|l[5]<<4);<br />
}<br />
<br />
inline uint8_t * board_to_eyes(uint8_t board[F][CYM][CXM]) {<br />
static uint8_t eyes[10]={0,0,0,0,0,0,0,0,0,0};<br />
eyes[0]=convert_l(board[1][5]);<br />
eyes[1]=convert_l(board[1][4]);<br />
eyes[2]=convert_l(board[1][3]);<br />
eyes[3]=convert_l(board[1][2]);<br />
eyes[4]=convert_l(board[1][1]);<br />
<br />
eyes[5]=convert_r(board[0][1]);<br />
eyes[6]=convert_r(board[0][2]);<br />
eyes[7]=convert_r(board[0][3]);<br />
eyes[8]=convert_r(board[0][4]);<br />
eyes[9]=convert_r(board[0][5]);<br />
<br />
return eyes;<br />
}<br />
<br />
<br />
void dump(uint8_t b[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
Serial.println("==");<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
Serial.print(b[k][j][i]?'x':'o');<br />
}<br />
Serial.println("");<br />
} <br />
Serial.println("==");<br />
}<br />
<br />
}<br />
<br />
void setup(){<br />
delay(2000);<br />
Serial.begin(9600);<br />
Serial.println("life");<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
irrecv.enableIRIn(); // Start the receiver<br />
clear(board_a); // start with empty board a<br />
clear(board_b); // start with empty board b<br />
//some initial state:<br />
board_a[0][1][2]=1;<br />
board_a[0][2][3]=1;<br />
board_a[0][3][1]=1;<br />
board_a[0][3][2]=1;<br />
board_a[0][3][3]=1;<br />
board_a[1][1][2]=1;<br />
board_a[1][2][3]=1;<br />
board_a[1][3][1]=1;<br />
board_a[1][3][2]=1;<br />
board_a[1][3][3]=1;<br />
Serial.println("inited");<br />
}<br />
<br />
inline void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21<br />
//6 7 8 9 10 20 19 18 17 16<br />
//11 12 13 14 15 15 14 13 12 11<br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
// dump(board_a);<br />
custom_eyes(4,board_to_eyes(board_a));<br />
play(board_a,board_b);<br />
delay(50);<br />
// dump(board_b);<br />
custom_eyes(4,board_to_eyes(board_b));<br />
play(board_b,board_a);<br />
delay(50);<br />
<br />
//TODO: add something to react to remote here, e.g. set some cells etc.<br />
if (irrecv.decode(&results)){<br />
irrecv.resume();<br />
} else {<br />
}<br />
}<br />
<br />
</pre><br />
<br />
==== i2c ====<br />
There might be more stuff connected to the i2c already, I plan to do some tests to find out. Would be nice if e.g. the flash of the mp3 player or some other expansion was already there.<br />
<br />
Regardless of what's there already, Vortex seems to be designed to be extended over i2c, and could be a nice platform to do some experiments/demo's with. <br />
<br />
Here's a simple scanning sketch, looks like only 0x40 has something connected, and that would be the eye-display.<br />
<br />
<pre><br />
#include <Wire.h><br />
<br />
<br />
void setup()<br />
{<br />
Wire.begin();<br />
<br />
Serial.begin(9600);<br />
while (!Serial); // Leonardo: wait for serial monitor<br />
Serial.println("\nI2C Scanner");<br />
}<br />
<br />
<br />
void loop()<br />
{<br />
byte error, address;<br />
int nDevices;<br />
<br />
Serial.println("Scanning...");<br />
<br />
nDevices = 0;<br />
for(address = 0; address < 128; address++ )<br />
{<br />
// The i2c_scanner uses the return value of<br />
// the Write.endTransmisstion to see if<br />
// a device did acknowledge to the address.<br />
Wire.beginTransmission(address);<br />
error = Wire.endTransmission();<br />
<br />
if (error == 0)<br />
{<br />
Serial.print("I2C device found at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
<br />
nDevices++;<br />
}<br />
else if (error==4)<br />
{<br />
Serial.print("Unknown error at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
} <br />
}<br />
if (nDevices == 0)<br />
Serial.println("No I2C devices found\n");<br />
else<br />
Serial.println("done\n");<br />
<br />
delay(5000); // wait 5 seconds for next scan<br />
}<br />
</pre><br />
<br />
====Bluetooth====<br />
It seems the Bluetooth functionality is the same as for the "Bluno" and thus the information at https://www.dfrobot.com/wiki/index.php/Bluno_SKU:DFR0267#Wireless_Programming_via_BLE applies.<br />
<br />
The AT commands seem to work, up to a point, but looks like it is a somewhat variant version of the firmware as AT+VERSION command doesn't work. Updating program seem not available for Linux.<br />
<br />
Booting the Vortex with the 'boot' button (near UL7, Vortex bottom-right-back RGB led.), two leds blink, and linux detects the Vortex over USB as:<br />
[230656.359259] usb 1-1.1: new full-speed USB device number 103 using xhci_hcd<br />
[230656.488502] usb 1-1.1: New USB device found, idVendor=2341, idProduct=0043<br />
[230656.488508] usb 1-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[230656.488511] usb 1-1.1: Product: DFRobot Boot CDC<br />
[230656.488513] usb 1-1.1: Manufacturer: DFRobot BLUno Boot<br />
[230656.490491] cdc_acm 1-1.1:1.0: ttyACM0: USB ACM device<br />
<br />
Similarly the little BLE dongle comes up as:<br />
[232626.931574] usb 1-1.3: new full-speed USB device number 108 using xhci_hcd<br />
[232627.254357] usb 1-1.3: New USB device found, idVendor=2341, idProduct=0043<br />
[232627.254362] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[232627.265758] cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device <br />
<br />
If Vortex pairs with the dongle, it effectiely gains an additional, wireless, serial link to a different port on the host. It's connected to the same Serial port on the internal Arduino. Make sure to adjust speed setting to what is used in the currently running sketch, the default 115200 won't work if Serial.begin(9600) is already called..<br />
<br />
<br />
Using AT commands it should be possible to set up the link connect to other Vortex robots too.<br />
<br />
Need to do some experiments to see if it is possible to connect to a generic Bluetooth 4.x dongle too.<br />
<br />
====Expansion====<br />
[[file:Vortex expansion holes drawing.svg|right|frame|hole arangement seen from top, vortex facing left or right]]<br />
There are 2 sets of 4 positions for screws/bolts at the top of the vortex, around the battery compartment, obviously to be used to put an expansion unit on top of Vortex, which could be linked to the i2c bus.<br />
<br />
=====Dimensions=====<br />
<br />
The bigger set of holes are 65 mm apart from left to right and 55 from front to back (center to center). These seem to be meant for 3 mm machine screws<br />
<br />
The smaller set of holes are 2.5" apart from left to right and 2.75" from front to back (center to center). These seem to be meant for small screws biting into the plastic.<br />
<br />
===== The 4 pin expansion port=====<br />
Q: Does Vortex support sensor module expansion?<br />
A: Vortex has a “Maker mode” where you can connect sensors via its IIC/TWI interface.<br />
<br />
It would seem the only logical place for this is the 4 pin connector in the back. Looking at the connector from the back of Vortex:<br />
____<br />
|1234|<br />
-__-<br />
<br />
These pins seem to be connected to the rainbow cable coming up from the main PCB as follows:<br />
<br />
{| class="wikitable"<br />
|+Vortex expansion port<br />
|-<br />
!Pin!!Function!!Color<br />
|-<br />
|1||SDA?||~15kOhm to Blue<br />
|-<br />
|2||SCL?||Blue<br />
|-<br />
|3||Gnd?||Black<br />
|-<br />
|4||Vcc?||Red<br />
|-<br />
|}<br />
<br />
It would make sense for Red to be 5V, Black to be Gnd, and the other two SCL and SDA of the i2c bus. This needs some further testing, preferably with a known good i2c device connected to the expansion port. The ~15kOhm between what I suspect to be SCL and SDA would then be two ~7.5kOhm to some common 5 V point that is buffered from the 'Red' cable.</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Vortex&diff=17572Luteijn/Vortex2018-02-10T16:47:17Z<p>Luteijn: </p>
<hr />
<div>===Project "Vortex":===<br />
{{Project<br />
|Name=Luteijn/Vortex<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=Vortex_1.jpg<br />
}}<br />
<br />
I've got four DFRobot 'Vortex' units. Basic programming via the official apps is possible, but rather limited. The WhenDo app is only available on iPad. Luckily, the internal Arduino clone can be directly programmed too. Unfortunately, not all the specs seem to be available, but there are some examples to work with, although they have a quite a bit of 'magic numbers' in them. These examples might disappear, so duplicating them here, with my own notes as available. The examples-coding is mostly done by "Andy Zhou <Andy.zhou@dfrobot.com>", although I did change some of the things to figure out what they do.<br />
<br />
http://wiki.dfrobot.com.cn/index.php?title=(SKU:ROB0116)_Vortex%E5%8F%AF%E7%BC%96%E7%A8%8B%E6%9C%BA%E5%99%A8%E4%BA%BA#.E6.A0.B7.E4.BE.8B.E4.BB.A3.E7.A0.81 <br />
<br />
https://www.dfrobot.com/wiki/index.php/Vortex_Arduino_Coding_Tutorial_V1.0#Introduction<br />
<br />
(Chinese page seems to have a bit better documentation, although English page might be easier to read and it is interesting to compare the differences)<br />
<br />
http://wiki.dfrobot.com.cn/images/1/10/%E4%B8%BB%E6%9D%BF.png<br />
<br />
==== What's connected to the pins ====<br />
{| class="wikitable"<br />
|+ Connection overview<br />
|-<br />
|0<br />
|Serial RX (input)<br />
|-<br />
|1<br />
|Serial TX (output)<br />
|-<br />
|2<br />
|Encoder Wheel (External Interrupt 0) (input)<br />
|-<br />
|3<br />
|Encoder Wheel (External Interrupt 1) (input)<br />
|-<br />
|4<br />
|Unknown, seems to be pulled high when used as input. Found it too hard to trace on the board.<br />
|-<br />
|5<br />
|Motor 0 Speed (PWM) (output)<br />
|-<br />
|6<br />
|Motor 1 Speed (PWM) (output)<br />
|-<br />
|7<br />
|IR decoder/detector (input)<br />
|-<br />
|8<br />
|IR led Left (output)<br />
|-<br />
|9<br />
|Motor 0 direction (H=forwards,L=reverse) (output)<br />
|-<br />
|10<br />
|Motor 1 direction (output)<br />
|-<br />
|11<br />
|MP3-player TX (output)<br />
|-<br />
|12<br />
|IR led Right (output)<br />
|-<br />
|13<br />
|GRB LED chain around body (output)<br />
|-<br />
|A0<br />
|grayscale 4 'D' (input)<br />
|-<br />
|A1<br />
|grayscale 3 'C' (input)<br />
|-<br />
|A2<br />
|grayscale 2 'B' (input)<br />
|-<br />
|A3<br />
|grayscale 1 'A' (input)<br />
|-<br />
|A4<br />
|SDA (i2c)<br />
|-<br />
|A5<br />
|SCL (i2c)<br />
|-<br />
|A6<br />
|grayscale 5 'E' (input)<br />
|-<br />
|A7<br />
|grayscale 6 'F' (input)<br />
|-<br />
|}<br />
<br />
i2c bus + Vcc & Gnd should be available at 4 pin rear expansion port too - need to find a suitable connector for it..<br />
<br />
http://www.hobbytronics.co.uk/arduino-atmega328-pinout Useful diagrams to map pins to package<br />
<br />
==== Dumping original firmware ====<br />
Although you can/should be able to reflash Vortex with the App, might as well make a backup of the orignal:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:r:"Vortex.hex":i<br />
This can be uploaded again with:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:w:"Vortex.hex":i<br />
(The app might also be initializing other things, but this seems to work)<br />
<br />
Your own sketches can be uploaded with <br />
arduino --upload MySketch.ino<br />
But using avrdude to upload the .hex file is a bit quicker.<br />
<br />
==== Motor Control ====<br />
<br />
Simple 'enable' and 'direction' pins for left and right wheels. Enable can be PWM'd or just full on.<br />
This example revs up the engines with PWM.<br />
<pre><br />
int E1 = 5; <br />
int M1 = 9; <br />
int E2 = 6; <br />
int M2 = 10; <br />
<br />
void setup() <br />
{ <br />
pinMode(M1, OUTPUT); // directional controls, High is forward, Low is backward <br />
pinMode(M2, OUTPUT); <br />
} <br />
<br />
void loop() <br />
{ <br />
int value; <br />
for(value = 0 ; value <= 255; value+=5) //foreward <br />
{ <br />
digitalWrite(M1,HIGH); <br />
digitalWrite(M2, HIGH); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
<br />
for(value = 0 ; value <= 255; value+=5) //backward <br />
{ <br />
digitalWrite(M1, LOW); <br />
digitalWrite(M2, LOW); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
} <br />
</pre><br />
<br />
====Encoders====<br />
The two external interrupts are linked to encoders that track wheel movement, so it is possible to detect the Vortext is being pushed/pulled/turned, although I don't think these things indicate which direction the robot is pushed in...<br />
<br />
<pre><br />
#define pinInputLeft 0 // this is the interrupt, not the pin number, D2<br />
#define pinInputRight 1 // external int 1, D3<br />
long leftPul,rightPul;<br />
<br />
void leftCallBack(){<br />
leftPul++;<br />
}<br />
<br />
void rightCallBack(){<br />
rightPul++;<br />
}<br />
<br />
void initDdevice(){<br />
pinMode(5,OUTPUT);<br />
pinMode(6,OUTPUT);<br />
pinMode(9,OUTPUT);<br />
pinMode(10,OUTPUT);<br />
noInterrupts();<br />
attachInterrupt(pinInputLeft,leftCallBack,CHANGE);<br />
attachInterrupt(pinInputRight,rightCallBack,CHANGE);<br />
interrupts();<br />
}<br />
<br />
void motorDebug(){<br />
digitalWrite(5,HIGH);<br />
digitalWrite(6,HIGH);<br />
digitalWrite(9,HIGH);<br />
digitalWrite(10,HIGH);<br />
}<br />
<br />
void printPul(){<br />
Serial.print(leftPul);<br />
Serial.print(" ");<br />
Serial.println(rightPul);<br />
leftPul = 0;<br />
rightPul = 0;<br />
}<br />
<br />
void setup() {<br />
initDdevice();<br />
Serial.begin(9600);<br />
motorDebug();<br />
}<br />
<br />
void loop() {<br />
printPul();<br />
delay(500);<br />
}<br />
</pre><br />
====Analogue Inputs====<br />
six 'grayscale' detectors are placed around the bottom of the Vortex, to be used to track a line, maybe read simple codes from the floor.<br />
<pre> <br />
2 (a2) 3 (a1) <br />
1 (a3) 4 (a0)<br />
<br />
<br />
<br />
<br />
5 (a6) 6 (a7)<br />
<br />
</pre><br />
This is the example sketch to dump the values read by the 6 little eyes.<br />
<pre><br />
void setup(void){<br />
Serial.begin(9600); <br />
}<br />
<br />
int analogBuf[6] = {'\0'};<br />
<br />
void loop(void){<br />
analogBuf[0] = analogRead(3);<br />
analogBuf[1] = analogRead(2);<br />
analogBuf[2] = analogRead(1);<br />
analogBuf[3] = analogRead(0);<br />
analogBuf[4] = analogRead(6);<br />
analogBuf[5] = analogRead(7);<br />
for(int i=0;i<6;i++){<br />
Serial.print(i+1);<br />
Serial.print(": ");<br />
Serial.print(analogBuf[i]);<br />
Serial.print(" ");<br />
}<br />
Serial.println();<br />
delay(500);<br />
}<br />
</pre><br />
<br />
Wonder what's connected to A4 and A5, if anything? These are used for the i2c bus! See below under the [[Luteijn/Vortex#Eyes]] section<br />
<br />
====LEDs====<br />
12 RGB (GRB!) leds can be addressed. Example from website causes Red and Green to be swapped. initialize library differently:<br />
<pre><br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
</pre><br />
IR example below uses some of the LED functionality.<br />
<br />
====IR obstacle detector====<br />
Seems to be somewhat flaky. Needs a bit more work to figure out. Also, the IR sensor is useful to read IR remote controllers or beacons. This receiver seems to work at 38kHz, but probabbly also reacts to 36-40kHz. Remote controllers often work at 36kHz. Note that 2x 8µs delay doesn't give you 38kHz, but the digitalWrite itself also induces quite some delay (3-6µs), so seems this is bringing the total period up to around the 26.something µs we'd expect.<br />
<br />
<br />
<pre><br />
#define NUM_LEDS 12 <br />
<br />
// Data pin that led data will be written out over <br />
#define DATA_PIN 13 <br />
CRGB leds[NUM_LEDS]; <br />
<br />
#define IR_IN 7//IR receiver pin <br />
#define L_IR 8 //left ir transmitter pin <br />
#define R_IR 12 //right ir transmitter pin <br />
<br />
int count; <br />
<br />
void leftSend38KHZ(void){//left ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(L_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(L_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
void rightSend38KHZ(void){//right ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(R_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(R_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
<br />
void pcint0Init(void){//init the interrupt <br />
PCICR |= 1 << PCIE2; <br />
PCMSK2 |= 1 << PCINT23; //pin D7 is int23 <br />
} <br />
<br />
ISR(PCINT2_vect){//IR decoder interrupt <br />
count++; <br />
} <br />
<br />
void obstacleAvoidance(void){ <br />
char i; <br />
count=0; <br />
leds[5] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){ //left transmitter sends 20 pulses <br />
leftSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[5] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Left"); <br />
delay(100); <br />
} <br />
count=0; <br />
leds[3] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){//right transmitter sends 20 pulses <br />
rightSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[3] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Right"); <br />
delay(100); <br />
} <br />
delay(600); <br />
} <br />
<br />
void setup(void){ <br />
delay(2000); <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
pinMode(L_IR,OUTPUT);//init the left transmitter pin <br />
pinMode(R_IR,OUTPUT);//init the right transmitter pin <br />
pinMode(IR_IN,INPUT);//init the ir receiver pin <br />
Serial.begin(9600); <br />
leds[4] = CRGB::Blue; <br />
FastLED.show(); <br />
delay(2000); <br />
leds[4] = CRGB::Purple; <br />
FastLED.show(); <br />
noInterrupts(); <br />
pcint0Init(); <br />
interrupts(); //enable the interrupt <br />
} <br />
<br />
void loop(void){ <br />
obstacleAvoidance(); <br />
}<br />
</pre><br />
<br />
==== IRreceiver ====<br />
Since DFrobot also have a 'loose' IR-receiver module in their program, decided to just see if the example sketches for those would transfer, and they do.<br />
https://www.dfrobot.com/product-366.html - A remote similar to the one pictured on the DFRobot product page actually came with one of my RTLSDR sticks, and seems to work well enough, although the range isn't great, couple of meters. With a 'proper' remote control, e.g. from a TV, the range is fine, 10m at least when pointing at unit from across the house. Might be less if using reflections to try to control a roving robot.<br />
<br />
So, using the IRremote library you can read values from a remote controller:<br />
<pre><br />
#include "IRremote.h"<br />
#define IR_IN 7//IR receiver pin<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
<br />
void setup(void){<br />
Serial.begin(9600); <br />
irrecv.enableIRIn(); // Start the receiver<br />
} <br />
<br />
void loop(void){ <br />
if (irrecv.decode(&results)) { <br />
Serial.println(results.value, HEX); <br />
irrecv.resume(); // Receive the next value<br />
} <br />
} <br />
</pre><br />
This was already useful to read codes from the remote of the AV-receiver we have at home and then play these back via a universal remote app on the smartphone that was missing some buttons. For some reason I couldn't find the 'learn' function in the app, but could enter the codes as read by this sketch easily.<br />
<br />
The receiver is mounted on the front, bottom. So not right between the eyes, although a spot seems to be prepared for it there too.<br />
<br />
The tables at [[Luteijn/IRRemotes]] might be useful when creating a sketch that is to be controlled over IR.<br />
<br />
Here's an example of a simple driving sketch controlled via my RTeL-Cheapo remote.<br />
<pre><br />
#include "IRremote.h"<br />
<br />
#define IR_IN (7)//IR receiver pin<br />
#define E1 (5)<br />
#define E2 (6)<br />
#define M1 (9)<br />
#define M2 (10)<br />
<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
// MSB is direction, 0xff full forward, 0x00 full backwards<br />
uint8_t Left=128; // 0x80 : forward, speed 0<br />
uint8_t Right=128; // 0x80 : forward, speed 0<br />
<br />
void setup(void){<br />
delay(2000); // grace period<br />
Serial.begin(9600);<br />
pinMode(M1, OUTPUT);<br />
pinMode(M2, OUTPUT);<br />
Engine(Left,Right);<br />
irrecv.enableIRIn(); // Start the receiver<br />
}<br />
<br />
void Engine(uint8_t Left, uint8_t Right) {<br />
uint8_t D1=Left&0x80;<br />
uint8_t D2=Right&0x80;<br />
uint8_t S1=D1?Left&0x7f:0x7f-(Left&0x7f);<br />
uint8_t S2=D2?Right&0x7f:0x7f-(Right&0x7f);<br />
Serial.print(Left,HEX);<br />
Serial.print("<-L R->");<br />
Serial.println(Right,HEX);<br />
digitalWrite(M1,D1);<br />
analogWrite(E1,S1<<1);<br />
digitalWrite(M2,D2);<br />
analogWrite(E2,S2<<1);<br />
}<br />
uint32_t last;<br />
void loop(void){<br />
uint32_t code;<br />
if (code=irrecv.decode(&results)) {<br />
code=results.value;<br />
Serial.print("Got IR:");<br />
Serial.println(code,HEX);<br />
<br />
if (code==0xFFFFFFFF) {code=last;}; // repeated press<br />
<br />
if (code==0xFFB24D) { // power = full stop<br />
Left=128;<br />
Right=128;<br />
} else if (code==0xFF02FD) { // full screen both go towards full stop<br />
if (Left<128) Left++; else Left--;<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFFA05F) { // ch+ increase both towards full ahead<br />
if (Left<255) Left++;<br />
if (Right<255) Right++;<br />
} else if (code==0xFF40BF) { // ch- decrease both towards full reverse<br />
if (Left>0) Left--;<br />
if (Right>0) Right--;<br />
} else if (code==0xFF50AF) { // vol- left towards full stop<br />
if (Left<128) Left++; else Left--;<br />
} else if (code==0xFF32CD) { // record increase left towards full ahead<br />
if (Left<255) Left++;<br />
} else if (code==0xFF48B7) { // 0 decrease left towards full reverse<br />
if (Left>0) Left--;<br />
} else if (code==0xFF7887) { // vol+ right towards full stop<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFF30CF) { // time shift increase right towards full ahead<br />
if (Right<255) Right++;<br />
} else if (code==0xFF38C7) { // recall decrease right towards full reverse<br />
if (Right>0) Right--;<br />
} else {<br />
// ignore unknown codes<br />
}<br />
last=code;<br />
irrecv.resume(); // Receive the next value<br />
Engine(Left,Right);<br />
}<br />
}<br />
</pre><br />
It would be better to use a stronger remote for this as it's hard to successfully control the robot when it's facing away from you. Also, the speed ramp up/down is a bit slow, and should probably take bigger steps than one at a time. <br />
<br />
Left as an exercise to the reader: add controls to spin in place left/right, using interrupts of wheel encoders to rotate 45/90 etc. degrees, boundary detection with analogue sensors, object detection with IR (careful not to make control harder). Switch to a bluetooth remote controller once figured out the possibilities of bluetooth better?<br />
<br />
====MP3-player====<br />
There is an MP3.player integrated in the robot. It can be controlled by sending more or less magic commands over software Serial via pin 11. Pin 2 might be connected to the player too if the example I found can be believed, but my tests so far never had anything received there, or on pin 4. Would be nice to get an 'end of song reached' or to get things like version information out. Anyway, Pin 2 is connected to one of the wheel encoders, so not likely to be the RX. It might also be co-connected to one of the relatively harmless outputs, like to the ir emitter? Will need to trace the board to find out, but initial quick look didn't immediately show anything.<br />
<br />
The example found on the DFRobot site:<br />
<pre><br />
#define MP3_VOLUME 0x10<br />
#define TX 11<br />
#define RX 4 // ?? was 2 in example I found, but that is unlikely<br />
#include <SoftwareSerial.h><br />
<br />
SoftwareSerial mySerial(RX, TX);// RX, TX<br />
void setup()<br />
{<br />
delay(1000); <br />
mp3Init();<br />
mp3setVolume(30);//0~30<br />
}<br />
void loop()<br />
{<br />
mp3player(1);<br />
delay(2000);<br />
mp3stop();<br />
delay(1000);<br />
}<br />
void mp3Init()<br />
{<br />
mySerial.begin (9600);<br />
}<br />
void mp3setVolume(byte vol)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
void mp3player(byte data)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
<br />
void mp3stop()<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x16, 0x00, 0x00, 0x00, 0xef};<br />
mySerial.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
The files to play can be put on the Vortex via usb, seems the little switch next to the micro-usb socket switches this between the mp3-players mass-storage and the arduino usb-serial interface. The number passed to the player is just the index into the (FAT?) table of stored songs. So, be careful of the order these are uploaded to the memory in. <br />
<br />
https://www.dfrobot.com/product-1121.html , documented at https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299 is a mini-MP3 player from DFRobot. It is controllable over serial, and might be the same one as used in the Vortex. Would be good to figure out if the 'busy' signal and/or the serial TX are connected back to the arduino somewhere in that case. Player could also be a variant but the magic numbers more or less match (although the stop command 0x16 is not in the table. <br />
<br />
The MP3-chip inside Vortex has Part# YX6100-24SS. Seems to be part of a family of serial MP3-players made by "广州悦欣电子科技有限公司" --> "Guangzhou Yue Xin Electronic Technology Co., Ltd." - website http://www.yx080.com/mp3xinpian/53-15.html has a download link on this yx6100 page but it seems to just refer us to the 5200 application Manual V1.8, which appears to be mostly compatible. The command table for the chip does include a 0x16 stop command. <br />
<br />
Also the datasheet mentions the chip can play both MP3 and WAV. YX5300-24SS is a similar part, datasheet at https://xp-dev.com/trac/arduino_antonio/export/280/arduino_antonio/trunk/EnterpriseBase/Serial%20MP3%20Player/About%20the%20Chip%20-%20YX5300/YX5300-24SS%20Datasheet%20V1.0.pdf The command table for that chip does include a 0x16 stop command.<br />
<br />
Documentation for the DFRobot miniplayer mentions the instruction format to be:<br />
{| class="wikitable"<br />
|+ Format: $S VER Len CMD Feedback para1 para2 checksum $O<br />
|-<br />
| $S<br />
| Start byte 0x7e<br />
| Each command begins with 0x7e ('~')<br />
|-<br />
| VER<br />
| Version information<br />
| Version seems to be 0xff from the example<br />
|-<br />
| Len<br />
| the number of bytes <br />
| Start, End and Checksum are not counted,<br />
|-<br />
| CMD<br />
| Commands<br />
| See command table.<br />
|-<br />
| Feedback<br />
| Command feedback<br />
| 1: feedback, 0: no feedback ; need to test what this does, might mean 'echo'<br />
|-<br />
| Para1<br />
| Parameter 1<br />
| Query high data byte<br />
|-<br />
| Para2<br />
| Parameter 2<br />
| Query low data byte<br />
|-<br />
| checksum<br />
| Checksum<br />
| accumulation and verification, doesn't include start byte. Seems to be 2 bytes from the example given in the docs, but Vortex seems to just not need it put in. example given is 7e ff 06 09 00 00 04 ff dd ef ; The 5200 Datasheet uses the exact same example command, and explains that you should add all the bytes, and then subtract those from 0 to get the checksum. The 5300 Datasheet also mentions "另外用户也可以直接忽视校验,参考我们的5.3.4 章节说明。" so looks like the checksum is optional if you don't care too much about corruption of the occasional command.<br />
|-<br />
| $O<br />
| End byte<br />
| End of command is signaled with 0xEF<br />
|}<br />
<br />
{| class="wikitable"<br />
|+Command table WIP to copy this over<br />
|-<br />
!CMD<br />
!Function Description<br />
!Parameters (16 bit)<br />
!compared with YX5300 info<br />
|-<br />
|0x01<br />
|Next<br />
|<br />
|-<br />
|0x02<br />
|Previous<br />
|<br />
|-<br />
|0x03<br />
|Specific track<br />
|0-2999 but example is only using low byte. also track 0 doesn't seem to do anything?<br />
|Indicates 1-255 are the valid tracks only.<br />
|-<br />
|0x04<br />
|Volume up<br />
|<br />
|-<br />
|0x05<br />
|Volume down<br />
|<br />
|-<br />
|0x06<br />
|Set Volume<br />
|0-30 (10 is already quite loud!)<br />
|-<br />
|0x07<br />
|Specify EQ(0/1/2/3/4/5/)<br />
|Normal/Pop/Rock/Jazz/Classic/Base.<br />
|Reserved in the 5300's command list.<br />
|-<br />
|0x08<br />
|Specify playback mode (0/1/2/3)<br />
|Repeat/folder repeat/single repeat/random<br />
|See 3.4.3; this explains the argument is the song to play in a loop - may have been changed for 6100<br />
|-<br />
|0x09<br />
|Select source (0/1/2/3/4)<br />
|U/TF/AUX/SLEEP/FLASH<br />
|See 3.4.4; specifies 1 as U, 2 as TF, 4 as PC, 5 FLASH and 6 SLEEP<br />
|-<br />
|0x0a<br />
|Enter into standby - low power loss<br />
|<br />
|Sleep - Low power consumption 10MA (obviously mA or µA, not MA, is meant)<br />
|-<br />
|0x0b<br />
|Normal working<br />
|<br />
|Wake up from sleep<br />
|-<br />
|0x0c<br />
|reset module<br />
|<br />
|chip reset<br />
|-<br />
|0x0d<br />
|Playback<br />
|<br />
|Play - seems to be contrast to suspend/pause<br />
|-<br />
|0x0e<br />
|Pause<br />
|<br />
|Suspended<br />
|-<br />
|0x0f<br />
|Specify folder to playback<br />
|1~10(need to set by user)<br />
|see 3.4.5 - DH: represents the name of the folder, the default support for 99 files, 01 - 99 named DL: represents the track, the default maximum of 255 songs, that is, 0x01 ~ 0xFF<br />
|-<br />
|0x10<br />
|Volume adjust set<br />
|DH=1:open volume adjust DL: set volume gain 0~31<br />
|not present in command table <br />
|-<br />
|0x11<br />
|Repeat play<br />
|1:start repeat play 0: stop play<br />
|not present in command table<br />
|-<br />
|0x16<br />
|not in the table<br />
|example mentions it as a stop command<br />
|Stop<br />
|-<br />
|0x17<br />
|not in the table<br />
|<br />
|FLASH only, see 3.4.7 (but is in section 3.4.6), select folder to loop.<br />
|-<br />
|0x18<br />
|not in the table<br />
|<br />
|Reserved<br />
|-<br />
|0x19<br />
|not in the table<br />
|<br />
|See 3.4.8 (but is in section 3.4.7), select loop current track mode<br />
|-<br />
|0x21<br />
|not in the table<br />
|<br />
|See 3.4.9 (3.4.8), set DAC to High-Z mode (1) or on (0) so you can use the amp for something else.<br />
|-<br />
|0x22<br />
|not in the table<br />
|<br />
|See 3.4.10 (section 3.4.9 doesn't exist), Set volume and song to play in one command (H:volume L:track)<br />
|-<br />
|0x3C<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3D<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3E<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3F<br />
|Send initialization parameters<br />
|0-0x0F (each bit represent one device of the low-four bits)<br />
|Seems to be reporting which storage devices are currently online (see section 3.5.1) <br />
|-<br />
|0x40<br />
|Returns an error, request retransmission<br />
|<br />
|Probably this is a response to a command meaning Error Encountered.<br />
|-<br />
|0x41<br />
|Answer<br />
|<br />
|Probably this is a response to a query and just means Accepted.<br />
|-<br />
|0x42<br />
|Query Status<br />
|<br />
|See 3.4.10 (actually 3.5.2) - explains that this will return which storage is used and if it's playing, stopped or paused. The storage could also be 'SLEEP' to indicate sleeping?<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
<br />
|}<br />
<br />
Code to calculate the checksum from the Library supporting the miniplayer:<br />
<pre><br />
uint16_t DFRobotDFPlayerMini::calculateCheckSum(uint8_t *buffer){<br />
uint16_t sum = 0;<br />
for (int i=Stack_Version; i<Stack_CheckSum; i++) {<br />
sum += buffer[i];<br />
}<br />
return -sum;<br />
}<br />
</pre><br />
but this doesn't seem to be needed.<br />
<br />
====Eyes====<br />
The 'Eyes' are connected over i2c. The daughterboard they are on features an STM8S103K3 (see http://www.st.com/en/microcontrollers/stm8s103-105.html ) and 2x HC595 (shift registers).<br />
Besides 35 predefined eye patterns, it is also possible to upload your own eye patterns.<br />
<br />
The default eyes can be set as follows:<br />
<pre><br />
#include <Wire.h><br />
#define I2C_LED_ADDRESS 0b1100000<br />
#define I2C_WRITE 0x00<br />
<br />
uint8_t serial=0;<br />
<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
defined_eyes(6,1); <br />
delay(2000);<br />
}<br />
void loop(){<br />
defined_eyes(1,serial); <br />
serial++; <br />
if(serial>=35) serial=0; <br />
delay(250); <br />
}<br />
<br />
<br />
void defined_eyes(uint8_t color,uint8_t serial) {<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4 <br />
// (I suppose they mean 0x40, although eyes seem to also respond on 0xC0 which is what is defined above, and 0x20)<br />
Wire.write(color&0x07); // color bits: 1 blue, 2 green, 4 red <br />
Wire.write(serial); //preset eyes 0~34<br />
Wire.endTransmission(); // stop transmitting <br />
}<br />
<br />
</pre><br />
<pre><br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21 <br />
//6 7 8 9 10 20 19 18 17 16 <br />
//11 12 13 14 15 15 14 13 12 11 <br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1 <br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4<br />
Wire.write(0x55); // color 55=custom eyes follow <br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
// other values cause eyes to light up in multiple colors, needs more work<br />
<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R of foreground<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
} <br />
Wire.endTransmission(); // stop transmitting <br />
<br />
<br />
void example_eyes() {<br />
/* left eye of robot (right for onlooker) */<br />
Wire.write(0x1f); // bottom row, all bits lit<br />
Wire.write(0x1e); // 10 9 8 7 on, 6 off<br />
Wire.write(0x1c);<br />
Wire.write(0x18);<br />
Wire.write(0x10);<br />
<br />
/* right eye of robot (left for onlooker) */<br />
Wire.write(0x1f); // top row all 5 bits lit<br />
Wire.write(0x0f); // leds 6,7,8,9 on 10 off<br />
Wire.write(0x07); // 11,12,13 on, 14,15 off <br />
Wire.write(0x03); <br />
Wire.write(0x01); <br />
} <br />
</pre><br />
<br />
=====KITT-scanner / Cylon=====<br />
This animates the eyes and adds some simple sound effects - currently the eye warble is not synchronized to the eye movement. The IR remote is used to trigger the well-known 'By your command'. For a KITT scanner may want to change to the proper woosh for a KITT, and replace the command sample with something like 'yes, Michael' :)<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <SoftwareSerial.h><br />
#include <IRremote.h><br />
<br />
#define MTX (11)<br />
#define MRX (4)<br />
<br />
// Song number for "By your command"-sample<br />
#define BYC (30)<br />
// Song number for Warble<br />
#define Warble (31)<br />
<br />
#define IR_IN (7)<br />
<br />
#define I2C_LED_ADDRESS 0x80<br />
bool left=1;<br />
decode_results results;<br />
SoftwareSerial mp3(MRX,MTX);<br />
IRrecv irrecv(IR_IN);<br />
short command=0;<br />
<br />
uint8_t eyes[10]={0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00};<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
mp3Init();<br />
irrecv.enableIRIn(); // Start the receiver<br />
delay(2000);<br />
delay(100);<br />
mp3setVolume(10);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(2); // repeat once<br />
delay(50);<br />
mp3player(BYC);<br />
delay(3000);<br />
mp3setVolume(8);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
delay(50);<br />
}<br />
void lscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]<<1);<br />
eyes[7]=(uint8_t)(eyes[7]<<1);<br />
}<br />
<br />
void rscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]>>1);<br />
eyes[7]=(uint8_t)(eyes[7]>>1);<br />
}<br />
<br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
<br />
// 1 2 4 8 10 '20' 10 8 4 2 1<br />
if (left) {lscroll_eyes(eyes);}<br />
else if (!left) {rscroll_eyes(eyes);}<br />
<br />
custom_eyes(4,eyes);<br />
<br />
if (eyes[7]==0x1) {left=1;}<br />
else if (eyes[2]==0x1) {left=1;}<br />
else if (left && eyes[2]>=0x10) {eyes[2]=0;eyes[7]=0x20;left=0;}<br />
else if (left && eyes[7]>=0x10) {eyes[7]=0;eyes[2]=0x20;left=0;}<br />
if (irrecv.decode(&results)){<br />
if (command) {<br />
command=0;<br />
} else{<br />
command=34; // adjust to match sample length<br />
mp3player(BYC);<br />
delay(50);<br />
}<br />
irrecv.resume();<br />
} else {<br />
if (command) {<br />
command--;<br />
if (!command){<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
} else {<br />
delay(80);<br />
}<br />
} else {<br />
delay(80);<br />
}<br />
}<br />
}<br />
<br />
void mp3Init()<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x0C, 0x00, 0x00, 0x00, 0xef};<br />
mp3.begin(9600);<br />
delay(100);<br />
mp3.write(buffer, 8);<br />
delay(100);<br />
}<br />
void mp3setVolume(byte vol)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3player(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3repeat(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x08, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
===== Conway's Game of Life =====<br />
2 5x5 fields linked as a torus of 10x5. Change mapping of cells in the edge to make e.g. a front and a back side, or change to 8x8 and add 4 more faces and a bit more mapping to create an LED cube of 6 faces. Simple animation on the 2 eye matrices. This version doesn't actually do something with the remote control, but it was easy to add things like reset board, change color, introduce disturbance etc.<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <IRremote.h><br />
<br />
#define IR_IN (7)<br />
#define I2C_LED_ADDRESS 0x80<br />
<br />
// 5x5 matrix eye<br />
#define CX (5)<br />
#define CY (5)<br />
// edge of 1 cell on each end<br />
#define CXM (7)<br />
#define CYM (7)<br />
<br />
#define F (2) // 2 faces have I.<br />
<br />
decode_results results;<br />
IRrecv irrecv(IR_IN);<br />
<br />
uint8_t eyes[10];<br />
<br />
uint8_t board_a[F][CYM][CXM];<br />
uint8_t board_b[F][CYM][CXM];<br />
<br />
<br />
inline void clear(uint8_t board[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
for (k=0;k<F;k++) {<br />
for (j=0;j<CYM;j++) {<br />
for (i=0;i<CXM;i++) {<br />
board[k][j][i]=0;<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t neighbors(uint8_t s[][CYM][CXM], uint8_t k,uint8_t j,uint8_t i) {<br />
return s[k][j-1][i-1]+s[k][j-1][i]+s[k][j-1][i+1]+s[k][j][i-1]+s[k][j][i+1]+s[k][j+1][i-1]+s[k][j+1][i]+s[k][j+1][i+1];<br />
}<br />
<br />
inline void play(uint8_t s[][CYM][CXM], uint8_t d[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
// TODO: load edges from right faces hardcoded for 5x5 now<br />
for (i=1;i<=5;i++) {<br />
s[0][0][i]=s[0][5][i];<br />
s[0][6][i]=s[0][1][i];<br />
<br />
s[0][i][0]=s[1][i][5];<br />
s[0][i][6]=s[1][i][1];<br />
<br />
s[1][0][i]=s[1][5][i];<br />
s[1][6][i]=s[1][1][i];<br />
<br />
s[1][i][0]=s[0][i][5];<br />
s[1][i][6]=s[0][i][1];<br />
<br />
}<br />
// corners<br />
s[0][0][0]=s[1][5][5];<br />
s[0][6][0]=s[1][1][5];<br />
<br />
s[0][0][6]=s[1][5][1];<br />
s[0][6][6]=s[1][1][1];<br />
<br />
s[1][0][0]=s[0][5][5];<br />
s[1][6][0]=s[0][1][5];<br />
<br />
s[1][0][6]=s[0][5][1];<br />
s[1][6][6]=s[0][1][1];<br />
<br />
<br />
// update interior cells<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
switch (neighbors(s,k,j,i)) {<br />
case 2: d[k][j][i]=s[k][j][i]; // remain<br />
break;<br />
case 3: d[k][j][i]=1; // birth<br />
break;<br />
default: d[k][j][i]=0; // death<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t convert_l(uint8_t l[CXM]) {<br />
return (l[5]|l[4]<<1|l[3]<<2|l[2]<<3|l[1]<<4);<br />
}<br />
inline uint8_t convert_r(uint8_t l[CXM]) {<br />
return (l[1]|l[2]<<1|l[3]<<2|l[4]<<3|l[5]<<4);<br />
}<br />
<br />
inline uint8_t * board_to_eyes(uint8_t board[F][CYM][CXM]) {<br />
static uint8_t eyes[10]={0,0,0,0,0,0,0,0,0,0};<br />
eyes[0]=convert_l(board[1][5]);<br />
eyes[1]=convert_l(board[1][4]);<br />
eyes[2]=convert_l(board[1][3]);<br />
eyes[3]=convert_l(board[1][2]);<br />
eyes[4]=convert_l(board[1][1]);<br />
<br />
eyes[5]=convert_r(board[0][1]);<br />
eyes[6]=convert_r(board[0][2]);<br />
eyes[7]=convert_r(board[0][3]);<br />
eyes[8]=convert_r(board[0][4]);<br />
eyes[9]=convert_r(board[0][5]);<br />
<br />
return eyes;<br />
}<br />
<br />
<br />
void dump(uint8_t b[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
Serial.println("==");<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
Serial.print(b[k][j][i]?'x':'o');<br />
}<br />
Serial.println("");<br />
} <br />
Serial.println("==");<br />
}<br />
<br />
}<br />
<br />
void setup(){<br />
delay(2000);<br />
Serial.begin(9600);<br />
Serial.println("life");<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
irrecv.enableIRIn(); // Start the receiver<br />
clear(board_a); // start with empty board a<br />
clear(board_b); // start with empty board b<br />
//some initial state:<br />
board_a[0][1][2]=1;<br />
board_a[0][2][3]=1;<br />
board_a[0][3][1]=1;<br />
board_a[0][3][2]=1;<br />
board_a[0][3][3]=1;<br />
board_a[1][1][2]=1;<br />
board_a[1][2][3]=1;<br />
board_a[1][3][1]=1;<br />
board_a[1][3][2]=1;<br />
board_a[1][3][3]=1;<br />
Serial.println("inited");<br />
}<br />
<br />
inline void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21<br />
//6 7 8 9 10 20 19 18 17 16<br />
//11 12 13 14 15 15 14 13 12 11<br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
// dump(board_a);<br />
custom_eyes(4,board_to_eyes(board_a));<br />
play(board_a,board_b);<br />
delay(50);<br />
// dump(board_b);<br />
custom_eyes(4,board_to_eyes(board_b));<br />
play(board_b,board_a);<br />
delay(50);<br />
<br />
//TODO: add something to react to remote here, e.g. set some cells etc.<br />
if (irrecv.decode(&results)){<br />
irrecv.resume();<br />
} else {<br />
}<br />
}<br />
<br />
</pre><br />
<br />
==== i2c ====<br />
There might be more stuff connected to the i2c already, I plan to do some tests to find out. Would be nice if e.g. the flash of the mp3 player or some other expansion was already there.<br />
<br />
Regardless of what's there already, Vortex seems to be designed to be extended over i2c, and could be a nice platform to do some experiments/demo's with. <br />
<br />
Here's a simple scanning sketch, looks like only 0x40 has something connected, and that would be the eye-display.<br />
<br />
<pre><br />
#include <Wire.h><br />
<br />
<br />
void setup()<br />
{<br />
Wire.begin();<br />
<br />
Serial.begin(9600);<br />
while (!Serial); // Leonardo: wait for serial monitor<br />
Serial.println("\nI2C Scanner");<br />
}<br />
<br />
<br />
void loop()<br />
{<br />
byte error, address;<br />
int nDevices;<br />
<br />
Serial.println("Scanning...");<br />
<br />
nDevices = 0;<br />
for(address = 0; address < 128; address++ )<br />
{<br />
// The i2c_scanner uses the return value of<br />
// the Write.endTransmisstion to see if<br />
// a device did acknowledge to the address.<br />
Wire.beginTransmission(address);<br />
error = Wire.endTransmission();<br />
<br />
if (error == 0)<br />
{<br />
Serial.print("I2C device found at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
<br />
nDevices++;<br />
}<br />
else if (error==4)<br />
{<br />
Serial.print("Unknown error at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
} <br />
}<br />
if (nDevices == 0)<br />
Serial.println("No I2C devices found\n");<br />
else<br />
Serial.println("done\n");<br />
<br />
delay(5000); // wait 5 seconds for next scan<br />
}<br />
</pre><br />
<br />
====Bluetooth====<br />
It seems the Bluetooth functionality is the same as for the "Bluno" and thus the information at https://www.dfrobot.com/wiki/index.php/Bluno_SKU:DFR0267#Wireless_Programming_via_BLE applies.<br />
<br />
The AT commands seem to work, up to a point, but looks like it is a somewhat variant version of the firmware as AT+VERSION command doesn't work. Updating program seem not available for Linux.<br />
<br />
Booting the Vortex with the 'boot' button (near UL7, Vortex bottom-right-back RGB led.), two leds blink, and linux detects the Vortex over USB as:<br />
[230656.359259] usb 1-1.1: new full-speed USB device number 103 using xhci_hcd<br />
[230656.488502] usb 1-1.1: New USB device found, idVendor=2341, idProduct=0043<br />
[230656.488508] usb 1-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[230656.488511] usb 1-1.1: Product: DFRobot Boot CDC<br />
[230656.488513] usb 1-1.1: Manufacturer: DFRobot BLUno Boot<br />
[230656.490491] cdc_acm 1-1.1:1.0: ttyACM0: USB ACM device<br />
<br />
Similarly the little BLE dongle comes up as:<br />
[232626.931574] usb 1-1.3: new full-speed USB device number 108 using xhci_hcd<br />
[232627.254357] usb 1-1.3: New USB device found, idVendor=2341, idProduct=0043<br />
[232627.254362] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[232627.265758] cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device <br />
<br />
If Vortex pairs with the dongle, it effectiely gains an additional, wireless, serial link to a different port on the host. It's connected to the same Serial port on the internal Arduino. Make sure to adjust speed setting to what is used in the currently running sketch, the default 115200 won't work if Serial.begin(9600) is already called..<br />
<br />
<br />
Using AT commands it should be possible to set up the link connect to other Vortex robots too.<br />
<br />
Need to do some experiments to see if it is possible to connect to a generic Bluetooth 4.x dongle too.<br />
<br />
====Expansion====<br />
[[file:Vortex expansion holes drawing.svg|right|frame|hole arangement seen from top, vortex facing left or right]]<br />
There are 2 sets of 4 positions for screws/bolts at the top of the vortex, around the battery compartment, obviously to be used to put an expansion unit on top of Vortex, which could be linked to the i2c bus.<br />
<br />
=====Dimensions=====<br />
<br />
The bigger set of holes are 65 mm apart from left to right and 55 from front to back (center to center). These seem to be meant for 3 mm machine screws<br />
<br />
The smaller set of holes are 2.5" apart from left to right and 2.75" from front to back (center to center). These seem to be meant for small screws biting into the plastic.<br />
<br />
===== The 4 pin expansion port=====<br />
Q: Does Vortex support sensor module expansion?<br />
A: Vortex has a “Maker mode” where you can connect sensors via its IIC/TWI interface.<br />
<br />
It would seem the only logical place for this is the 4 pin connector in the back. Looking at the connector from the back of Vortex:<br />
____<br />
|1234|<br />
-__-<br />
<br />
These pins seem to be connected to the rainbow cable coming up from the main PCB as follows:<br />
<br />
{| class="wikitable"<br />
|+Vortex expansion bus<br />
|-<br />
!Pin!!Function!!Color<br />
|-<br />
|1||?||~15kOhm to Blue<br />
|-<br />
|2||Vcc?||Blue<br />
|-<br />
|3||?||Black<br />
|-<br />
|4||?||Red<br />
|-<br />
|}<br />
<br />
It would make sense for Red to be 5V, Black to be Gnd, and the other two SCL and SDA of the i2c bus, but this doesn't seem to be the case. Will need to test a bit more to find out more.</div>Luteijnhttps://revspace.nl/index.php?title=Luteijn/Vortex&diff=17571Luteijn/Vortex2018-02-10T16:40:50Z<p>Luteijn: /* The 4 pin expansion port */</p>
<hr />
<div>===Project "Vortex":===<br />
{{Project<br />
|Name=Luteijn/Vortex<br />
|Status=In progress <br />
|Contact=Luteijn<br />
|Picture=Vortex_1.jpg<br />
}}<br />
<br />
I've got four DFRobot 'Vortex' units. Basic programming via the official apps is possible, but rather limited. The WhenDo app is only available on iPad. Luckily, the internal Arduino clone can be directly programmed too. Unfortunately, not all the specs seem to be available, but there are some examples to work with, although they have a quite a bit of 'magic numbers' in them. These examples might disappear, so duplicating them here, with my own notes as available. The examples-coding is mostly done by "Andy Zhou <Andy.zhou@dfrobot.com>", although I did change some of the things to figure out what they do.<br />
<br />
http://wiki.dfrobot.com.cn/index.php?title=(SKU:ROB0116)_Vortex%E5%8F%AF%E7%BC%96%E7%A8%8B%E6%9C%BA%E5%99%A8%E4%BA%BA#.E6.A0.B7.E4.BE.8B.E4.BB.A3.E7.A0.81 <br />
<br />
https://www.dfrobot.com/wiki/index.php/Vortex_Arduino_Coding_Tutorial_V1.0#Introduction<br />
<br />
(Chinese page seems to have a bit better documentation, although English page might be easier to read and it is interesting to compare the differences)<br />
<br />
http://wiki.dfrobot.com.cn/images/1/10/%E4%B8%BB%E6%9D%BF.png<br />
<br />
==== What's connected to the pins ====<br />
{| class="wikitable"<br />
|+ Connection overview<br />
|-<br />
|0<br />
|Serial RX (input)<br />
|-<br />
|1<br />
|Serial TX (output)<br />
|-<br />
|2<br />
|Encoder Wheel (External Interrupt 0) (input)<br />
|-<br />
|3<br />
|Encoder Wheel (External Interrupt 1) (input)<br />
|-<br />
|4<br />
|Unknown, seems to be pulled high when used as input. Found it too hard to trace on the board.<br />
|-<br />
|5<br />
|Motor 0 Speed (PWM) (output)<br />
|-<br />
|6<br />
|Motor 1 Speed (PWM) (output)<br />
|-<br />
|7<br />
|IR decoder/detector (input)<br />
|-<br />
|8<br />
|IR led Left (output)<br />
|-<br />
|9<br />
|Motor 0 direction (H=forwards,L=reverse) (output)<br />
|-<br />
|10<br />
|Motor 1 direction (output)<br />
|-<br />
|11<br />
|MP3-player TX (output)<br />
|-<br />
|12<br />
|IR led Right (output)<br />
|-<br />
|13<br />
|GRB LED chain around body (output)<br />
|-<br />
|A0<br />
|grayscale 4 'D' (input)<br />
|-<br />
|A1<br />
|grayscale 3 'C' (input)<br />
|-<br />
|A2<br />
|grayscale 2 'B' (input)<br />
|-<br />
|A3<br />
|grayscale 1 'A' (input)<br />
|-<br />
|A4<br />
|SDA (i2c)<br />
|-<br />
|A5<br />
|SCL (i2c)<br />
|-<br />
|A6<br />
|grayscale 5 'E' (input)<br />
|-<br />
|A7<br />
|grayscale 6 'F' (input)<br />
|-<br />
|}<br />
<br />
i2c bus + Vcc & Gnd should be available at 4 pin rear expansion port too - need to find a suitable connector for it..<br />
<br />
http://www.hobbytronics.co.uk/arduino-atmega328-pinout Useful diagrams to map pins to package<br />
<br />
==== Dumping original firmware ====<br />
Although you can/should be able to reflash Vortex with the App, might as well make a backup of the orignal:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:r:"Vortex.hex":i<br />
This can be uploaded again with:<br />
avrdude -c arduino -p m328p -P /dev/ttyACM0 -U flash:w:"Vortex.hex":i<br />
(The app might also be initializing other things, but this seems to work)<br />
<br />
Your own sketches can be uploaded with <br />
arduino --upload MySketch.ino<br />
But using avrdude to upload the .hex file is a bit quicker.<br />
<br />
==== Motor Control ====<br />
<br />
Simple 'enable' and 'direction' pins for left and right wheels. Enable can be PWM'd or just full on.<br />
This example revs up the engines with PWM.<br />
<pre><br />
int E1 = 5; <br />
int M1 = 9; <br />
int E2 = 6; <br />
int M2 = 10; <br />
<br />
void setup() <br />
{ <br />
pinMode(M1, OUTPUT); // directional controls, High is forward, Low is backward <br />
pinMode(M2, OUTPUT); <br />
} <br />
<br />
void loop() <br />
{ <br />
int value; <br />
for(value = 0 ; value <= 255; value+=5) //foreward <br />
{ <br />
digitalWrite(M1,HIGH); <br />
digitalWrite(M2, HIGH); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
<br />
for(value = 0 ; value <= 255; value+=5) //backward <br />
{ <br />
digitalWrite(M1, LOW); <br />
digitalWrite(M2, LOW); <br />
analogWrite(E1, value); //PWM Speed control <br />
analogWrite(E2, value); //PWM Speed Control <br />
delay(30); <br />
} <br />
} <br />
</pre><br />
<br />
====Encoders====<br />
The two external interrupts are linked to encoders that track wheel movement, so it is possible to detect the Vortext is being pushed/pulled/turned, although I don't think these things indicate which direction the robot is pushed in...<br />
<br />
<pre><br />
#define pinInputLeft 0 // this is the interrupt, not the pin number, D2<br />
#define pinInputRight 1 // external int 1, D3<br />
long leftPul,rightPul;<br />
<br />
void leftCallBack(){<br />
leftPul++;<br />
}<br />
<br />
void rightCallBack(){<br />
rightPul++;<br />
}<br />
<br />
void initDdevice(){<br />
pinMode(5,OUTPUT);<br />
pinMode(6,OUTPUT);<br />
pinMode(9,OUTPUT);<br />
pinMode(10,OUTPUT);<br />
noInterrupts();<br />
attachInterrupt(pinInputLeft,leftCallBack,CHANGE);<br />
attachInterrupt(pinInputRight,rightCallBack,CHANGE);<br />
interrupts();<br />
}<br />
<br />
void motorDebug(){<br />
digitalWrite(5,HIGH);<br />
digitalWrite(6,HIGH);<br />
digitalWrite(9,HIGH);<br />
digitalWrite(10,HIGH);<br />
}<br />
<br />
void printPul(){<br />
Serial.print(leftPul);<br />
Serial.print(" ");<br />
Serial.println(rightPul);<br />
leftPul = 0;<br />
rightPul = 0;<br />
}<br />
<br />
void setup() {<br />
initDdevice();<br />
Serial.begin(9600);<br />
motorDebug();<br />
}<br />
<br />
void loop() {<br />
printPul();<br />
delay(500);<br />
}<br />
</pre><br />
====Analogue Inputs====<br />
six 'grayscale' detectors are placed around the bottom of the Vortex, to be used to track a line, maybe read simple codes from the floor.<br />
<pre> <br />
2 (a2) 3 (a1) <br />
1 (a3) 4 (a0)<br />
<br />
<br />
<br />
<br />
5 (a6) 6 (a7)<br />
<br />
</pre><br />
This is the example sketch to dump the values read by the 6 little eyes.<br />
<pre><br />
void setup(void){<br />
Serial.begin(9600); <br />
}<br />
<br />
int analogBuf[6] = {'\0'};<br />
<br />
void loop(void){<br />
analogBuf[0] = analogRead(3);<br />
analogBuf[1] = analogRead(2);<br />
analogBuf[2] = analogRead(1);<br />
analogBuf[3] = analogRead(0);<br />
analogBuf[4] = analogRead(6);<br />
analogBuf[5] = analogRead(7);<br />
for(int i=0;i<6;i++){<br />
Serial.print(i+1);<br />
Serial.print(": ");<br />
Serial.print(analogBuf[i]);<br />
Serial.print(" ");<br />
}<br />
Serial.println();<br />
delay(500);<br />
}<br />
</pre><br />
<br />
Wonder what's connected to A4 and A5, if anything? These are used for the i2c bus! See below under the [[Luteijn/Vortex#Eyes]] section<br />
<br />
====LEDs====<br />
12 RGB (GRB!) leds can be addressed. Example from website causes Red and Green to be swapped. initialize library differently:<br />
<pre><br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
</pre><br />
IR example below uses some of the LED functionality.<br />
<br />
====IR obstacle detector====<br />
Seems to be somewhat flaky. Needs a bit more work to figure out. Also, the IR sensor is useful to read IR remote controllers or beacons. This receiver seems to work at 38kHz, but probabbly also reacts to 36-40kHz. Remote controllers often work at 36kHz. Note that 2x 8µs delay doesn't give you 38kHz, but the digitalWrite itself also induces quite some delay (3-6µs), so seems this is bringing the total period up to around the 26.something µs we'd expect.<br />
<br />
<br />
<pre><br />
#define NUM_LEDS 12 <br />
<br />
// Data pin that led data will be written out over <br />
#define DATA_PIN 13 <br />
CRGB leds[NUM_LEDS]; <br />
<br />
#define IR_IN 7//IR receiver pin <br />
#define L_IR 8 //left ir transmitter pin <br />
#define R_IR 12 //right ir transmitter pin <br />
<br />
int count; <br />
<br />
void leftSend38KHZ(void){//left ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(L_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(L_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
void rightSend38KHZ(void){//right ir transmitter sends 38kHZ pulse <br />
int i; <br />
for(i=0;i<24;i++){ <br />
digitalWrite(R_IR,LOW); <br />
delayMicroseconds(8); <br />
digitalWrite(R_IR,HIGH); <br />
delayMicroseconds(8); <br />
} <br />
} <br />
<br />
void pcint0Init(void){//init the interrupt <br />
PCICR |= 1 << PCIE2; <br />
PCMSK2 |= 1 << PCINT23; //pin D7 is int23 <br />
} <br />
<br />
ISR(PCINT2_vect){//IR decoder interrupt <br />
count++; <br />
} <br />
<br />
void obstacleAvoidance(void){ <br />
char i; <br />
count=0; <br />
leds[5] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){ //left transmitter sends 20 pulses <br />
leftSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[5] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Left"); <br />
delay(100); <br />
} <br />
count=0; <br />
leds[3] = CRGB::Green; <br />
FastLED.show(); <br />
for(i=0;i<20;i++){//right transmitter sends 20 pulses <br />
rightSend38KHZ(); <br />
delayMicroseconds(600); <br />
} <br />
if(count>10){//if received a lot pulse , it means there's a obstacle <br />
leds[3] = CRGB::White; <br />
FastLED.show(); <br />
Serial.println("Right"); <br />
delay(100); <br />
} <br />
delay(600); <br />
} <br />
<br />
void setup(void){ <br />
delay(2000); <br />
FastLED.addLeds<WS2811, DATA_PIN, GRB>(leds, NUM_LEDS); <br />
pinMode(L_IR,OUTPUT);//init the left transmitter pin <br />
pinMode(R_IR,OUTPUT);//init the right transmitter pin <br />
pinMode(IR_IN,INPUT);//init the ir receiver pin <br />
Serial.begin(9600); <br />
leds[4] = CRGB::Blue; <br />
FastLED.show(); <br />
delay(2000); <br />
leds[4] = CRGB::Purple; <br />
FastLED.show(); <br />
noInterrupts(); <br />
pcint0Init(); <br />
interrupts(); //enable the interrupt <br />
} <br />
<br />
void loop(void){ <br />
obstacleAvoidance(); <br />
}<br />
</pre><br />
<br />
==== IRreceiver ====<br />
Since DFrobot also have a 'loose' IR-receiver module in their program, decided to just see if the example sketches for those would transfer, and they do.<br />
https://www.dfrobot.com/product-366.html - A remote similar to the one pictured on the DFRobot product page actually came with one of my RTLSDR sticks, and seems to work well enough, although the range isn't great, couple of meters. With a 'proper' remote control, e.g. from a TV, the range is fine, 10m at least when pointing at unit from across the house. Might be less if using reflections to try to control a roving robot.<br />
<br />
So, using the IRremote library you can read values from a remote controller:<br />
<pre><br />
#include "IRremote.h"<br />
#define IR_IN 7//IR receiver pin<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
<br />
void setup(void){<br />
Serial.begin(9600); <br />
irrecv.enableIRIn(); // Start the receiver<br />
} <br />
<br />
void loop(void){ <br />
if (irrecv.decode(&results)) { <br />
Serial.println(results.value, HEX); <br />
irrecv.resume(); // Receive the next value<br />
} <br />
} <br />
</pre><br />
This was already useful to read codes from the remote of the AV-receiver we have at home and then play these back via a universal remote app on the smartphone that was missing some buttons. For some reason I couldn't find the 'learn' function in the app, but could enter the codes as read by this sketch easily.<br />
<br />
The receiver is mounted on the front, bottom. So not right between the eyes, although a spot seems to be prepared for it there too.<br />
<br />
The tables at [[Luteijn/IRRemotes]] might be useful when creating a sketch that is to be controlled over IR.<br />
<br />
Here's an example of a simple driving sketch controlled via my RTeL-Cheapo remote.<br />
<pre><br />
#include "IRremote.h"<br />
<br />
#define IR_IN (7)//IR receiver pin<br />
#define E1 (5)<br />
#define E2 (6)<br />
#define M1 (9)<br />
#define M2 (10)<br />
<br />
IRrecv irrecv(IR_IN);<br />
decode_results results;<br />
// MSB is direction, 0xff full forward, 0x00 full backwards<br />
uint8_t Left=128; // 0x80 : forward, speed 0<br />
uint8_t Right=128; // 0x80 : forward, speed 0<br />
<br />
void setup(void){<br />
delay(2000); // grace period<br />
Serial.begin(9600);<br />
pinMode(M1, OUTPUT);<br />
pinMode(M2, OUTPUT);<br />
Engine(Left,Right);<br />
irrecv.enableIRIn(); // Start the receiver<br />
}<br />
<br />
void Engine(uint8_t Left, uint8_t Right) {<br />
uint8_t D1=Left&0x80;<br />
uint8_t D2=Right&0x80;<br />
uint8_t S1=D1?Left&0x7f:0x7f-(Left&0x7f);<br />
uint8_t S2=D2?Right&0x7f:0x7f-(Right&0x7f);<br />
Serial.print(Left,HEX);<br />
Serial.print("<-L R->");<br />
Serial.println(Right,HEX);<br />
digitalWrite(M1,D1);<br />
analogWrite(E1,S1<<1);<br />
digitalWrite(M2,D2);<br />
analogWrite(E2,S2<<1);<br />
}<br />
uint32_t last;<br />
void loop(void){<br />
uint32_t code;<br />
if (code=irrecv.decode(&results)) {<br />
code=results.value;<br />
Serial.print("Got IR:");<br />
Serial.println(code,HEX);<br />
<br />
if (code==0xFFFFFFFF) {code=last;}; // repeated press<br />
<br />
if (code==0xFFB24D) { // power = full stop<br />
Left=128;<br />
Right=128;<br />
} else if (code==0xFF02FD) { // full screen both go towards full stop<br />
if (Left<128) Left++; else Left--;<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFFA05F) { // ch+ increase both towards full ahead<br />
if (Left<255) Left++;<br />
if (Right<255) Right++;<br />
} else if (code==0xFF40BF) { // ch- decrease both towards full reverse<br />
if (Left>0) Left--;<br />
if (Right>0) Right--;<br />
} else if (code==0xFF50AF) { // vol- left towards full stop<br />
if (Left<128) Left++; else Left--;<br />
} else if (code==0xFF32CD) { // record increase left towards full ahead<br />
if (Left<255) Left++;<br />
} else if (code==0xFF48B7) { // 0 decrease left towards full reverse<br />
if (Left>0) Left--;<br />
} else if (code==0xFF7887) { // vol+ right towards full stop<br />
if (Right<128) Right++; else Right--;<br />
} else if (code==0xFF30CF) { // time shift increase right towards full ahead<br />
if (Right<255) Right++;<br />
} else if (code==0xFF38C7) { // recall decrease right towards full reverse<br />
if (Right>0) Right--;<br />
} else {<br />
// ignore unknown codes<br />
}<br />
last=code;<br />
irrecv.resume(); // Receive the next value<br />
Engine(Left,Right);<br />
}<br />
}<br />
</pre><br />
It would be better to use a stronger remote for this as it's hard to successfully control the robot when it's facing away from you. Also, the speed ramp up/down is a bit slow, and should probably take bigger steps than one at a time. <br />
<br />
Left as an exercise to the reader: add controls to spin in place left/right, using interrupts of wheel encoders to rotate 45/90 etc. degrees, boundary detection with analogue sensors, object detection with IR (careful not to make control harder). Switch to a bluetooth remote controller once figured out the possibilities of bluetooth better?<br />
<br />
====MP3-player====<br />
There is an MP3.player integrated in the robot. It can be controlled by sending more or less magic commands over software Serial via pin 11. Pin 2 might be connected to the player too if the example I found can be believed, but my tests so far never had anything received there, or on pin 4. Would be nice to get an 'end of song reached' or to get things like version information out. Anyway, Pin 2 is connected to one of the wheel encoders, so not likely to be the RX. It might also be co-connected to one of the relatively harmless outputs, like to the ir emitter? Will need to trace the board to find out, but initial quick look didn't immediately show anything.<br />
<br />
The example found on the DFRobot site:<br />
<pre><br />
#define MP3_VOLUME 0x10<br />
#define TX 11<br />
#define RX 4 // ?? was 2 in example I found, but that is unlikely<br />
#include <SoftwareSerial.h><br />
<br />
SoftwareSerial mySerial(RX, TX);// RX, TX<br />
void setup()<br />
{<br />
delay(1000); <br />
mp3Init();<br />
mp3setVolume(30);//0~30<br />
}<br />
void loop()<br />
{<br />
mp3player(1);<br />
delay(2000);<br />
mp3stop();<br />
delay(1000);<br />
}<br />
void mp3Init()<br />
{<br />
mySerial.begin (9600);<br />
}<br />
void mp3setVolume(byte vol)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
void mp3player(byte data)<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mySerial.write(buffer, 8);<br />
delay(20);<br />
}<br />
<br />
void mp3stop()<br />
{<br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x16, 0x00, 0x00, 0x00, 0xef};<br />
mySerial.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
The files to play can be put on the Vortex via usb, seems the little switch next to the micro-usb socket switches this between the mp3-players mass-storage and the arduino usb-serial interface. The number passed to the player is just the index into the (FAT?) table of stored songs. So, be careful of the order these are uploaded to the memory in. <br />
<br />
https://www.dfrobot.com/product-1121.html , documented at https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299 is a mini-MP3 player from DFRobot. It is controllable over serial, and might be the same one as used in the Vortex. Would be good to figure out if the 'busy' signal and/or the serial TX are connected back to the arduino somewhere in that case. Player could also be a variant but the magic numbers more or less match (although the stop command 0x16 is not in the table. <br />
<br />
The MP3-chip inside Vortex has Part# YX6100-24SS. Seems to be part of a family of serial MP3-players made by "广州悦欣电子科技有限公司" --> "Guangzhou Yue Xin Electronic Technology Co., Ltd." - website http://www.yx080.com/mp3xinpian/53-15.html has a download link on this yx6100 page but it seems to just refer us to the 5200 application Manual V1.8, which appears to be mostly compatible. The command table for the chip does include a 0x16 stop command. <br />
<br />
Also the datasheet mentions the chip can play both MP3 and WAV. YX5300-24SS is a similar part, datasheet at https://xp-dev.com/trac/arduino_antonio/export/280/arduino_antonio/trunk/EnterpriseBase/Serial%20MP3%20Player/About%20the%20Chip%20-%20YX5300/YX5300-24SS%20Datasheet%20V1.0.pdf The command table for that chip does include a 0x16 stop command.<br />
<br />
Documentation for the DFRobot miniplayer mentions the instruction format to be:<br />
{| class="wikitable"<br />
|+ Format: $S VER Len CMD Feedback para1 para2 checksum $O<br />
|-<br />
| $S<br />
| Start byte 0x7e<br />
| Each command begins with 0x7e ('~')<br />
|-<br />
| VER<br />
| Version information<br />
| Version seems to be 0xff from the example<br />
|-<br />
| Len<br />
| the number of bytes <br />
| Start, End and Checksum are not counted,<br />
|-<br />
| CMD<br />
| Commands<br />
| See command table.<br />
|-<br />
| Feedback<br />
| Command feedback<br />
| 1: feedback, 0: no feedback ; need to test what this does, might mean 'echo'<br />
|-<br />
| Para1<br />
| Parameter 1<br />
| Query high data byte<br />
|-<br />
| Para2<br />
| Parameter 2<br />
| Query low data byte<br />
|-<br />
| checksum<br />
| Checksum<br />
| accumulation and verification, doesn't include start byte. Seems to be 2 bytes from the example given in the docs, but Vortex seems to just not need it put in. example given is 7e ff 06 09 00 00 04 ff dd ef ; The 5200 Datasheet uses the exact same example command, and explains that you should add all the bytes, and then subtract those from 0 to get the checksum. The 5300 Datasheet also mentions "另外用户也可以直接忽视校验,参考我们的5.3.4 章节说明。" so looks like the checksum is optional if you don't care too much about corruption of the occasional command.<br />
|-<br />
| $O<br />
| End byte<br />
| End of command is signaled with 0xEF<br />
|}<br />
<br />
{| class="wikitable"<br />
|+Command table WIP to copy this over<br />
|-<br />
!CMD<br />
!Function Description<br />
!Parameters (16 bit)<br />
!compared with YX5300 info<br />
|-<br />
|0x01<br />
|Next<br />
|<br />
|-<br />
|0x02<br />
|Previous<br />
|<br />
|-<br />
|0x03<br />
|Specific track<br />
|0-2999 but example is only using low byte. also track 0 doesn't seem to do anything?<br />
|Indicates 1-255 are the valid tracks only.<br />
|-<br />
|0x04<br />
|Volume up<br />
|<br />
|-<br />
|0x05<br />
|Volume down<br />
|<br />
|-<br />
|0x06<br />
|Set Volume<br />
|0-30 (10 is already quite loud!)<br />
|-<br />
|0x07<br />
|Specify EQ(0/1/2/3/4/5/)<br />
|Normal/Pop/Rock/Jazz/Classic/Base.<br />
|Reserved in the 5300's command list.<br />
|-<br />
|0x08<br />
|Specify playback mode (0/1/2/3)<br />
|Repeat/folder repeat/single repeat/random<br />
|See 3.4.3; this explains the argument is the song to play in a loop - may have been changed for 6100<br />
|-<br />
|0x09<br />
|Select source (0/1/2/3/4)<br />
|U/TF/AUX/SLEEP/FLASH<br />
|See 3.4.4; specifies 1 as U, 2 as TF, 4 as PC, 5 FLASH and 6 SLEEP<br />
|-<br />
|0x0a<br />
|Enter into standby - low power loss<br />
|<br />
|Sleep - Low power consumption 10MA (obviously mA or µA, not MA, is meant)<br />
|-<br />
|0x0b<br />
|Normal working<br />
|<br />
|Wake up from sleep<br />
|-<br />
|0x0c<br />
|reset module<br />
|<br />
|chip reset<br />
|-<br />
|0x0d<br />
|Playback<br />
|<br />
|Play - seems to be contrast to suspend/pause<br />
|-<br />
|0x0e<br />
|Pause<br />
|<br />
|Suspended<br />
|-<br />
|0x0f<br />
|Specify folder to playback<br />
|1~10(need to set by user)<br />
|see 3.4.5 - DH: represents the name of the folder, the default support for 99 files, 01 - 99 named DL: represents the track, the default maximum of 255 songs, that is, 0x01 ~ 0xFF<br />
|-<br />
|0x10<br />
|Volume adjust set<br />
|DH=1:open volume adjust DL: set volume gain 0~31<br />
|not present in command table <br />
|-<br />
|0x11<br />
|Repeat play<br />
|1:start repeat play 0: stop play<br />
|not present in command table<br />
|-<br />
|0x16<br />
|not in the table<br />
|example mentions it as a stop command<br />
|Stop<br />
|-<br />
|0x17<br />
|not in the table<br />
|<br />
|FLASH only, see 3.4.7 (but is in section 3.4.6), select folder to loop.<br />
|-<br />
|0x18<br />
|not in the table<br />
|<br />
|Reserved<br />
|-<br />
|0x19<br />
|not in the table<br />
|<br />
|See 3.4.8 (but is in section 3.4.7), select loop current track mode<br />
|-<br />
|0x21<br />
|not in the table<br />
|<br />
|See 3.4.9 (3.4.8), set DAC to High-Z mode (1) or on (0) so you can use the amp for something else.<br />
|-<br />
|0x22<br />
|not in the table<br />
|<br />
|See 3.4.10 (section 3.4.9 doesn't exist), Set volume and song to play in one command (H:volume L:track)<br />
|-<br />
|0x3C<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3D<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3E<br />
|STAY<br />
|<br />
|Reserved<br />
|-<br />
|0x3F<br />
|Send initialization parameters<br />
|0-0x0F (each bit represent one device of the low-four bits)<br />
|Seems to be reporting which storage devices are currently online (see section 3.5.1) <br />
|-<br />
|0x40<br />
|Returns an error, request retransmission<br />
|<br />
|Probably this is a response to a command meaning Error Encountered.<br />
|-<br />
|0x41<br />
|Answer<br />
|<br />
|Probably this is a response to a query and just means Accepted.<br />
|-<br />
|0x42<br />
|Query Status<br />
|<br />
|See 3.4.10 (actually 3.5.2) - explains that this will return which storage is used and if it's playing, stopped or paused. The storage could also be 'SLEEP' to indicate sleeping?<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
|-<br />
|<br />
|<br />
|<br />
<br />
|}<br />
<br />
Code to calculate the checksum from the Library supporting the miniplayer:<br />
<pre><br />
uint16_t DFRobotDFPlayerMini::calculateCheckSum(uint8_t *buffer){<br />
uint16_t sum = 0;<br />
for (int i=Stack_Version; i<Stack_CheckSum; i++) {<br />
sum += buffer[i];<br />
}<br />
return -sum;<br />
}<br />
</pre><br />
but this doesn't seem to be needed.<br />
<br />
====Eyes====<br />
The 'Eyes' are connected over i2c. The daughterboard they are on features an STM8S103K3 (see http://www.st.com/en/microcontrollers/stm8s103-105.html ) and 2x HC595 (shift registers).<br />
Besides 35 predefined eye patterns, it is also possible to upload your own eye patterns.<br />
<br />
The default eyes can be set as follows:<br />
<pre><br />
#include <Wire.h><br />
#define I2C_LED_ADDRESS 0b1100000<br />
#define I2C_WRITE 0x00<br />
<br />
uint8_t serial=0;<br />
<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
defined_eyes(6,1); <br />
delay(2000);<br />
}<br />
void loop(){<br />
defined_eyes(1,serial); <br />
serial++; <br />
if(serial>=35) serial=0; <br />
delay(250); <br />
}<br />
<br />
<br />
void defined_eyes(uint8_t color,uint8_t serial) {<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4 <br />
// (I suppose they mean 0x40, although eyes seem to also respond on 0xC0 which is what is defined above, and 0x20)<br />
Wire.write(color&0x07); // color bits: 1 blue, 2 green, 4 red <br />
Wire.write(serial); //preset eyes 0~34<br />
Wire.endTransmission(); // stop transmitting <br />
}<br />
<br />
</pre><br />
<pre><br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21 <br />
//6 7 8 9 10 20 19 18 17 16 <br />
//11 12 13 14 15 15 14 13 12 11 <br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1 <br />
Wire.beginTransmission(I2C_LED_ADDRESS << 1 | I2C_WRITE); // transmit to device #4<br />
Wire.write(0x55); // color 55=custom eyes follow <br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
// other values cause eyes to light up in multiple colors, needs more work<br />
<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R of foreground<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
} <br />
Wire.endTransmission(); // stop transmitting <br />
<br />
<br />
void example_eyes() {<br />
/* left eye of robot (right for onlooker) */<br />
Wire.write(0x1f); // bottom row, all bits lit<br />
Wire.write(0x1e); // 10 9 8 7 on, 6 off<br />
Wire.write(0x1c);<br />
Wire.write(0x18);<br />
Wire.write(0x10);<br />
<br />
/* right eye of robot (left for onlooker) */<br />
Wire.write(0x1f); // top row all 5 bits lit<br />
Wire.write(0x0f); // leds 6,7,8,9 on 10 off<br />
Wire.write(0x07); // 11,12,13 on, 14,15 off <br />
Wire.write(0x03); <br />
Wire.write(0x01); <br />
} <br />
</pre><br />
<br />
=====KITT-scanner / Cylon=====<br />
This animates the eyes and adds some simple sound effects - currently the eye warble is not synchronized to the eye movement. The IR remote is used to trigger the well-known 'By your command'. For a KITT scanner may want to change to the proper woosh for a KITT, and replace the command sample with something like 'yes, Michael' :)<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <SoftwareSerial.h><br />
#include <IRremote.h><br />
<br />
#define MTX (11)<br />
#define MRX (4)<br />
<br />
// Song number for "By your command"-sample<br />
#define BYC (30)<br />
// Song number for Warble<br />
#define Warble (31)<br />
<br />
#define IR_IN (7)<br />
<br />
#define I2C_LED_ADDRESS 0x80<br />
bool left=1;<br />
decode_results results;<br />
SoftwareSerial mp3(MRX,MTX);<br />
IRrecv irrecv(IR_IN);<br />
short command=0;<br />
<br />
uint8_t eyes[10]={0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00};<br />
void setup(){<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
mp3Init();<br />
irrecv.enableIRIn(); // Start the receiver<br />
delay(2000);<br />
delay(100);<br />
mp3setVolume(10);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(2); // repeat once<br />
delay(50);<br />
mp3player(BYC);<br />
delay(3000);<br />
mp3setVolume(8);//0~255, but 30 is already quite loud!<br />
delay(50);<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
delay(50);<br />
}<br />
void lscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]<<1);<br />
eyes[7]=(uint8_t)(eyes[7]<<1);<br />
}<br />
<br />
void rscroll_eyes(uint8_t * eyes ){<br />
uint8_t i;<br />
eyes[2]=(uint8_t)(eyes[2]>>1);<br />
eyes[7]=(uint8_t)(eyes[7]>>1);<br />
}<br />
<br />
void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
<br />
// 1 2 4 8 10 '20' 10 8 4 2 1<br />
if (left) {lscroll_eyes(eyes);}<br />
else if (!left) {rscroll_eyes(eyes);}<br />
<br />
custom_eyes(4,eyes);<br />
<br />
if (eyes[7]==0x1) {left=1;}<br />
else if (eyes[2]==0x1) {left=1;}<br />
else if (left && eyes[2]>=0x10) {eyes[2]=0;eyes[7]=0x20;left=0;}<br />
else if (left && eyes[7]>=0x10) {eyes[7]=0;eyes[2]=0x20;left=0;}<br />
if (irrecv.decode(&results)){<br />
if (command) {<br />
command=0;<br />
} else{<br />
command=34; // adjust to match sample length<br />
mp3player(BYC);<br />
delay(50);<br />
}<br />
irrecv.resume();<br />
} else {<br />
if (command) {<br />
command--;<br />
if (!command){<br />
mp3player(Warble);<br />
delay(50);<br />
mp3repeat(0); // repeat forever<br />
} else {<br />
delay(80);<br />
}<br />
} else {<br />
delay(80);<br />
}<br />
}<br />
}<br />
<br />
void mp3Init()<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x0C, 0x00, 0x00, 0x00, 0xef};<br />
mp3.begin(9600);<br />
delay(100);<br />
mp3.write(buffer, 8);<br />
delay(100);<br />
}<br />
void mp3setVolume(byte vol)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x06, 0x00, 0x00, vol, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3player(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x03, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
void mp3repeat(byte data)<br />
{ <br />
uint8_t buffer[] = {0x7e, 0xff, 0x06, 0x08, 0x00, 0x00, data, 0xef};<br />
mp3.write(buffer, 8);<br />
}<br />
</pre><br />
<br />
===== Conway's Game of Life =====<br />
2 5x5 fields linked as a torus of 10x5. Change mapping of cells in the edge to make e.g. a front and a back side, or change to 8x8 and add 4 more faces and a bit more mapping to create an LED cube of 6 faces. Simple animation on the 2 eye matrices. This version doesn't actually do something with the remote control, but it was easy to add things like reset board, change color, introduce disturbance etc.<br />
<br />
<pre><br />
#include <Wire.h><br />
#include <IRremote.h><br />
<br />
#define IR_IN (7)<br />
#define I2C_LED_ADDRESS 0x80<br />
<br />
// 5x5 matrix eye<br />
#define CX (5)<br />
#define CY (5)<br />
// edge of 1 cell on each end<br />
#define CXM (7)<br />
#define CYM (7)<br />
<br />
#define F (2) // 2 faces have I.<br />
<br />
decode_results results;<br />
IRrecv irrecv(IR_IN);<br />
<br />
uint8_t eyes[10];<br />
<br />
uint8_t board_a[F][CYM][CXM];<br />
uint8_t board_b[F][CYM][CXM];<br />
<br />
<br />
inline void clear(uint8_t board[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
for (k=0;k<F;k++) {<br />
for (j=0;j<CYM;j++) {<br />
for (i=0;i<CXM;i++) {<br />
board[k][j][i]=0;<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t neighbors(uint8_t s[][CYM][CXM], uint8_t k,uint8_t j,uint8_t i) {<br />
return s[k][j-1][i-1]+s[k][j-1][i]+s[k][j-1][i+1]+s[k][j][i-1]+s[k][j][i+1]+s[k][j+1][i-1]+s[k][j+1][i]+s[k][j+1][i+1];<br />
}<br />
<br />
inline void play(uint8_t s[][CYM][CXM], uint8_t d[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
// TODO: load edges from right faces hardcoded for 5x5 now<br />
for (i=1;i<=5;i++) {<br />
s[0][0][i]=s[0][5][i];<br />
s[0][6][i]=s[0][1][i];<br />
<br />
s[0][i][0]=s[1][i][5];<br />
s[0][i][6]=s[1][i][1];<br />
<br />
s[1][0][i]=s[1][5][i];<br />
s[1][6][i]=s[1][1][i];<br />
<br />
s[1][i][0]=s[0][i][5];<br />
s[1][i][6]=s[0][i][1];<br />
<br />
}<br />
// corners<br />
s[0][0][0]=s[1][5][5];<br />
s[0][6][0]=s[1][1][5];<br />
<br />
s[0][0][6]=s[1][5][1];<br />
s[0][6][6]=s[1][1][1];<br />
<br />
s[1][0][0]=s[0][5][5];<br />
s[1][6][0]=s[0][1][5];<br />
<br />
s[1][0][6]=s[0][5][1];<br />
s[1][6][6]=s[0][1][1];<br />
<br />
<br />
// update interior cells<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
switch (neighbors(s,k,j,i)) {<br />
case 2: d[k][j][i]=s[k][j][i]; // remain<br />
break;<br />
case 3: d[k][j][i]=1; // birth<br />
break;<br />
default: d[k][j][i]=0; // death<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
inline uint8_t convert_l(uint8_t l[CXM]) {<br />
return (l[5]|l[4]<<1|l[3]<<2|l[2]<<3|l[1]<<4);<br />
}<br />
inline uint8_t convert_r(uint8_t l[CXM]) {<br />
return (l[1]|l[2]<<1|l[3]<<2|l[4]<<3|l[5]<<4);<br />
}<br />
<br />
inline uint8_t * board_to_eyes(uint8_t board[F][CYM][CXM]) {<br />
static uint8_t eyes[10]={0,0,0,0,0,0,0,0,0,0};<br />
eyes[0]=convert_l(board[1][5]);<br />
eyes[1]=convert_l(board[1][4]);<br />
eyes[2]=convert_l(board[1][3]);<br />
eyes[3]=convert_l(board[1][2]);<br />
eyes[4]=convert_l(board[1][1]);<br />
<br />
eyes[5]=convert_r(board[0][1]);<br />
eyes[6]=convert_r(board[0][2]);<br />
eyes[7]=convert_r(board[0][3]);<br />
eyes[8]=convert_r(board[0][4]);<br />
eyes[9]=convert_r(board[0][5]);<br />
<br />
return eyes;<br />
}<br />
<br />
<br />
void dump(uint8_t b[][CYM][CXM]) {<br />
uint8_t i,j,k;<br />
Serial.println("==");<br />
for (k=0;k<F;k++) {<br />
for (j=1;j<=CY;j++) {<br />
for (i=1;i<=CX;i++) {<br />
Serial.print(b[k][j][i]?'x':'o');<br />
}<br />
Serial.println("");<br />
} <br />
Serial.println("==");<br />
}<br />
<br />
}<br />
<br />
void setup(){<br />
delay(2000);<br />
Serial.begin(9600);<br />
Serial.println("life");<br />
Wire.begin(); // join i2c bus (address optional for master)<br />
irrecv.enableIRIn(); // Start the receiver<br />
clear(board_a); // start with empty board a<br />
clear(board_b); // start with empty board b<br />
//some initial state:<br />
board_a[0][1][2]=1;<br />
board_a[0][2][3]=1;<br />
board_a[0][3][1]=1;<br />
board_a[0][3][2]=1;<br />
board_a[0][3][3]=1;<br />
board_a[1][1][2]=1;<br />
board_a[1][2][3]=1;<br />
board_a[1][3][1]=1;<br />
board_a[1][3][2]=1;<br />
board_a[1][3][3]=1;<br />
Serial.println("inited");<br />
}<br />
<br />
inline void custom_eyes(uint8_t color, uint8_t eyeline[10]){<br />
uint8_t index;<br />
// right eye left eye<br />
//1 2 3 4 5 25 24 23 22 21<br />
//6 7 8 9 10 20 19 18 17 16<br />
//11 12 13 14 15 15 14 13 12 11<br />
//16 17 18 19 20 10 9 8 7 6<br />
//21 22 23 24 25 5 4 3 2 1<br />
<br />
Wire.beginTransmission(I2C_LED_ADDRESS); // 0x4 preshifted to 0x8 and lsb set to 0 for write<br />
Wire.write(0x55); // register 55=?custom eyes<br />
Wire.write(0xAA); // ?? AA=color followed by 10 bytes, each defining one line<br />
Wire.write(color&0x7); //color 1-B,2-G,4-R<br />
<br />
for (index=0;index<10;index++) {<br />
Wire.write(eyeline[index]);<br />
}<br />
Wire.endTransmission(); // stop transmitting<br />
}<br />
<br />
void loop(){<br />
// dump(board_a);<br />
custom_eyes(4,board_to_eyes(board_a));<br />
play(board_a,board_b);<br />
delay(50);<br />
// dump(board_b);<br />
custom_eyes(4,board_to_eyes(board_b));<br />
play(board_b,board_a);<br />
delay(50);<br />
<br />
//TODO: add something to react to remote here, e.g. set some cells etc.<br />
if (irrecv.decode(&results)){<br />
irrecv.resume();<br />
} else {<br />
}<br />
}<br />
<br />
</pre><br />
<br />
==== i2c ====<br />
There might be more stuff connected to the i2c already, I plan to do some tests to find out. Would be nice if e.g. the flash of the mp3 player or some other expansion was already there.<br />
<br />
Regardless of what's there already, Vortex seems to be designed to be extended over i2c, and could be a nice platform to do some experiments/demo's with. <br />
<br />
Here's a simple scanning sketch, looks like only 0x40 has something connected, and that would be the eye-display.<br />
<br />
<pre><br />
#include <Wire.h><br />
<br />
<br />
void setup()<br />
{<br />
Wire.begin();<br />
<br />
Serial.begin(9600);<br />
while (!Serial); // Leonardo: wait for serial monitor<br />
Serial.println("\nI2C Scanner");<br />
}<br />
<br />
<br />
void loop()<br />
{<br />
byte error, address;<br />
int nDevices;<br />
<br />
Serial.println("Scanning...");<br />
<br />
nDevices = 0;<br />
for(address = 0; address < 128; address++ )<br />
{<br />
// The i2c_scanner uses the return value of<br />
// the Write.endTransmisstion to see if<br />
// a device did acknowledge to the address.<br />
Wire.beginTransmission(address);<br />
error = Wire.endTransmission();<br />
<br />
if (error == 0)<br />
{<br />
Serial.print("I2C device found at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
<br />
nDevices++;<br />
}<br />
else if (error==4)<br />
{<br />
Serial.print("Unknown error at address 0x");<br />
if (address<16)<br />
Serial.print("0");<br />
Serial.print(address,HEX);<br />
Serial.print(" (");<br />
Serial.print(address,DEC);<br />
Serial.println("d) !");<br />
} <br />
}<br />
if (nDevices == 0)<br />
Serial.println("No I2C devices found\n");<br />
else<br />
Serial.println("done\n");<br />
<br />
delay(5000); // wait 5 seconds for next scan<br />
}<br />
</pre><br />
<br />
====Bluetooth====<br />
It seems the Bluetooth functionality is the same as for the "Bluno" and thus the information at https://www.dfrobot.com/wiki/index.php/Bluno_SKU:DFR0267#Wireless_Programming_via_BLE applies.<br />
<br />
The AT commands seem to work, up to a point, but looks like it is a somewhat variant version of the firmware as AT+VERSION command doesn't work. Updating program seem not available for Linux.<br />
<br />
Booting the Vortex with the 'boot' button (near UL7, Vortex bottom-right-back RGB led.), two leds blink, and linux detects the Vortex over USB as:<br />
[230656.359259] usb 1-1.1: new full-speed USB device number 103 using xhci_hcd<br />
[230656.488502] usb 1-1.1: New USB device found, idVendor=2341, idProduct=0043<br />
[230656.488508] usb 1-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[230656.488511] usb 1-1.1: Product: DFRobot Boot CDC<br />
[230656.488513] usb 1-1.1: Manufacturer: DFRobot BLUno Boot<br />
[230656.490491] cdc_acm 1-1.1:1.0: ttyACM0: USB ACM device<br />
<br />
Similarly the little BLE dongle comes up as:<br />
[232626.931574] usb 1-1.3: new full-speed USB device number 108 using xhci_hcd<br />
[232627.254357] usb 1-1.3: New USB device found, idVendor=2341, idProduct=0043<br />
[232627.254362] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3<br />
[232627.265758] cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device <br />
<br />
If Vortex pairs with the dongle, it effectiely gains an additional, wireless, serial link to a different port on the host. It's connected to the same Serial port on the internal Arduino. Make sure to adjust speed setting to what is used in the currently running sketch, the default 115200 won't work if Serial.begin(9600) is already called..<br />
<br />
<br />
Using AT commands it should be possible to set up the link connect to other Vortex robots too.<br />
<br />
Need to do some experiments to see if it is possible to connect to a generic Bluetooth 4.x dongle too.<br />
<br />
====Expansion====<br />
[[file:Vortex expansion holes drawing.svg|right|frame|hole arangement seen from top, vortex facing left or right]]<br />
There are 2 sets of 4 positions for screws/bolts at the top of the vortex, around the battery compartment, obviously to be used to put an expansion unit on top of Vortex, which could be linked to the i2c bus.<br />
<br />
=====Dimensions=====<br />
<br />
The bigger set of holes are 65 mm apart from left to right and 55 from front to back (center to center). These seem to be meant for 3 mm machine screws<br />
<br />
The smaller set of holes are 2.5" apart from left to right and 2.75" from front to back (center to center). These seem to be meant for small screws biting into the plastic.<br />
<br />
===== The 4 pin expansion port=====<br />
Q: Does Vortex support sensor module expansion?<br />
A: Vortex has a “Maker mode” where you can connect sensors via its IIC/TWI interface.<br />
<br />
It would seem the only logical place for this is the 4 pin connector in the back. Looking at the connector from the back of Vortex:<br />
____<br />
|1234|<br />
-__-<br />
<br />
These pins seem to be connected to the rainbow cable coming up form the main PCB as follows:<br />
<br />
{| class="wikitable"<br />
|+Vortex expansion bus<br />
|-<br />
!Pin!!Function!!Color<br />
|-<br />
|1||?||~15kOhm to Blue<br />
|-<br />
|2||Vcc?||Blue<br />
|-<br />
|3||?||Black<br />
|-<br />
|4||?||Red<br />
|-<br />
|}<br />
<br />
It would make sense for Red to be 5V, Black to be Gnd, and the other two SCL and SDA of the i2c bus, but this doesn't seem to be the case. Will need to test a bit more to find out more.</div>Luteijn