📜 ⬆️ ⬇️

Practical application of MSP430 for web-developer

On Habré plenty of articles for beginners about how magical and wonderful this MSP430 LaunchPad from Texas Instruments. However, the standard flashing lights LED usually no one goes. It is time to correct this situation.
Working as a team, we use the good old SVN for version control. It would seem, where does the microcontroller?
Just to signal the next commit to the repository, I adapted this marvelous thing.



Idea

I would like a device that will signal melody about the next commit in any of our repositories. It would be nice if each developer had his own unique melody, and everyone would hear who had just made another commit.

Project

As a web frontend to the SVN is bolted WebSVN, closed Basic-authorization. Thus, we can access the list of projects and their revisions. In turn, we need a process that, according to a schedule, for example, once a minute will check the project repositories for a new commit. Since PHP was the one closest to the hands, it was decided to write on it. The working system is Windows 7 with Apache 2.1 + PHP 5.3.6.
When a new commit is detected, the script sends a control command to the device to the COM port, indicating that the melody of the commit author should be played.
')
So, we have a WebSVN:



Using PHP, we parse the list of projects and look into each project:
index.php
<? //     set_time_limit(0); //      require 'functions.php'; require 'config.php'; //  CURL $s = curl_init(); curl_setopt($s, CURLOPT_URL, $svnURL); curl_setopt($s, CURLOPT_USERPWD, $authLogin.':'.$authPass); curl_setopt($s, CURLOPT_USERAGENT, 'CommitBeep 1.0'); curl_setopt($s, CURLOPT_REFERER, $svnURL); curl_setopt($s, CURLOPT_RETURNTRANSFER, true); //    $page = curl_exec($s); $httpCode = curl_getinfo($s, CURLINFO_HTTP_CODE); curl_close($s); //     $doc = new DOMDocument(); $doc->loadHTML($page); $links = $doc->getElementsByTagName('a'); foreach ($links as $link) { $link = dom2array($link); if (strpos($link['@attributes']['href'], 'listing.php?repname') !== false) { $projectName = $link['#text']; checkSVNCommit($projectName); } } 

In order to log in via Basic Auth, use the functions of the CURL library.
We also use DOMDocument and DOM to parse HTML.

Since working with DOM from PHP is a bit of a chore, and we just need to take all the links, we use the dom2array () function:
functions.php dom2array ()
 /** *  DOM    * @param object $node  */ function dom2array($node) { $res = array(); if($node->nodeType == XML_TEXT_NODE){ $res = $node->nodeValue; } else{ if($node->hasAttributes()){ $attributes = $node->attributes; if(!is_null($attributes)){ $res['@attributes'] = array(); foreach ($attributes as $index=>$attr) { $res['@attributes'][$attr->name] = $attr->value; } } } if($node->hasChildNodes()){ $children = $node->childNodes; for($i=0;$i<$children->length;$i++){ $child = $children->item($i); $res[$child->nodeName] = dom2array($child); } } } return $res; } 


Each project has its own page, which contains the latest revision and its author.



Using the same CURL, pick out the revision number and its author. Parsing through explode () and strpos () is terrible, but it will pull you over.
functions.php checkSVNCommit ()
 /** *     ,   * @param string $projectName   */ function checkSVNCommit($projectName) { require 'config.php'; //       $old_rev = @file_get_contents('revisions/'.$projectName.'.txt'); $rev = 0; $user = 'unknown'; //  CURL $s = curl_init(); curl_setopt($s, CURLOPT_URL, $svnURL.'/listing.php?repname='.urlencode($projectName).'&path=%2F&sc=0'); curl_setopt($s, CURLOPT_USERPWD, $authLogin.':'.$authPass); curl_setopt($s, CURLOPT_USERAGENT, 'CommitBeep 1.0'); curl_setopt($s, CURLOPT_REFERER, $svnURL); curl_setopt($s, CURLOPT_RETURNTRANSFER, true); //    $page = curl_exec($s); $httpCode = curl_getinfo($s, CURLINFO_HTTP_CODE); curl_close($s); //   ,      $page = explode("\n", strip_tags($page)); foreach($page as $line) { if (strpos($line, 'Last modification:') !== false) { $line = explode(' ', $line); $rev = $line[3]; $user = $line[5]; } } echo "$projectName $rev $user<br/>\n"; //  ? ! if ($rev != $old_rev) { beep($comNumber, getMelodyByUser($user)); file_put_contents('revisions/'.$projectName.'.txt', $rev); } } 


The implementation is banal and clumsy in places, but it works great. We read the old revision from the text file and compare it with the current one. If different, write and bibikayem.

First pitfalls

I was 146% sure that working with COM ports is easy. As it turned out, the old fopen method (“COM6:”, “w +”) on Windows 7 no longer works. Some access rights are missing. In addition, even if you redirect the output to the port in the console, an access error will occur again. So from cmd (bat) - the files will not work either, and it will not roll through exec () either.
Strong googling brought me to the Windows extension - PHP Serial.
It connects like all extensions, quite simple and works like two fingers:
functions.php beep ()
 /** *    * @param integer $com  COM- * @param integer $melody   */ function beep($com = 6, $melody = 1) { ser_open("COM".$com, 9600, 8, "None", "1", "None"); ser_write("$melody"); ser_close(); } 


Bibikalo with a microcontroller

It is the turn to program LaunchPad. Take Energia as a development environment.
Unlike the Arduino, for the MSP430 is not as obvious as working with the Serial port. It turns out that the TimerSerial library, which is a modification of the Arduino-vskoy base library of Serial, suffices.
A little effort, and you can write and read in the terminal.
An example of working with the terminal
 #include <TimerSerial.h> TimerSerial mySerial; //   void setup() { mySerial.begin(); mySerial.println("Welcome to CommitBeep 1.0"); } //   void loop() { while (mySerial.available()) { char inChar = (char)mySerial.read(); mySerial.write(inChar); delay(100); } } 


This simple little program reads the character and immediately writes it back. Like an echo.

Underwater rocks. Continued.

So, with the reception and transmission of data sorted out. Now bibikalo.
The standard library of Tone, also somehow modified in Energia. And the most unpleasant, they are not friends with TimerSerial. Probably, general timers are used or something else in this spirit, but together they could not be used.
I had to write my bike.

Tame bibikalo
 #define NOTE_G3 196 #define NOTE_A3 220 #define NOTE_C4 262 int speakerPin; //   void setup() { speakerPin = 14; //  1.6 ( 14)      pinMode(speakerPin, OUTPUT); beep(); } void playTone(int tone, int duration) { for (long i = 0; i < duration * 1000L; i += tone * 2) { digitalWrite(speakerPin, HIGH); delayMicroseconds(tone); digitalWrite(speakerPin, LOW); delayMicroseconds(tone); } digitalWrite(speakerPin, LOW); } //  void beep() { int melody[] = {NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3}; int noteDurations[] = {4,8,8,4,4}; for (int thisNote = 0; thisNote < 4; thisNote++) { int noteDuration = 1000/noteDurations[thisNote]; playTone(melody[thisNote], noteDuration); } } 


All problems solved, it remains to write the code. In general, the rest is quite simple.
Combine and get our sketch:
commit_beep.ino
 #include <TimerSerial.h> #define NOTE_B0 31 #define NOTE_C1 33 #define NOTE_CS1 35 #define NOTE_D1 37 #define NOTE_DS1 39 #define NOTE_E1 41 #define NOTE_F1 44 #define NOTE_FS1 46 #define NOTE_G1 49 #define NOTE_GS1 52 #define NOTE_A1 55 #define NOTE_AS1 58 #define NOTE_B1 62 #define NOTE_C2 65 #define NOTE_CS2 69 #define NOTE_D2 73 #define NOTE_DS2 78 #define NOTE_E2 82 #define NOTE_F2 87 #define NOTE_FS2 93 #define NOTE_G2 98 #define NOTE_GS2 104 #define NOTE_A2 110 #define NOTE_AS2 117 #define NOTE_B2 123 #define NOTE_C3 131 #define NOTE_CS3 139 #define NOTE_D3 147 #define NOTE_DS3 156 #define NOTE_E3 165 #define NOTE_F3 175 #define NOTE_FS3 185 #define NOTE_G3 196 #define NOTE_GS3 208 #define NOTE_A3 220 #define NOTE_AS3 233 #define NOTE_B3 247 #define NOTE_C4 262 #define NOTE_CS4 277 #define NOTE_D4 294 #define NOTE_DS4 311 #define NOTE_E4 330 #define NOTE_F4 349 #define NOTE_FS4 370 #define NOTE_G4 392 #define NOTE_GS4 415 #define NOTE_A4 440 #define NOTE_AS4 466 #define NOTE_B4 494 #define NOTE_C5 523 #define NOTE_CS5 554 #define NOTE_D5 587 #define NOTE_DS5 622 #define NOTE_E5 659 #define NOTE_F5 698 #define NOTE_FS5 740 #define NOTE_G5 784 #define NOTE_GS5 831 #define NOTE_A5 880 #define NOTE_AS5 932 #define NOTE_B5 988 #define NOTE_C6 1047 #define NOTE_CS6 1109 #define NOTE_D6 1175 #define NOTE_DS6 1245 #define NOTE_E6 1319 #define NOTE_F6 1397 #define NOTE_FS6 1480 #define NOTE_G6 1568 #define NOTE_GS6 1661 #define NOTE_A6 1760 #define NOTE_AS6 1865 #define NOTE_B6 1976 #define NOTE_C7 2093 #define NOTE_CS7 2217 #define NOTE_D7 2349 #define NOTE_DS7 2489 #define NOTE_E7 2637 #define NOTE_F7 2794 #define NOTE_FS7 2960 #define NOTE_G7 3136 #define NOTE_GS7 3322 #define NOTE_A7 3520 #define NOTE_AS7 3729 #define NOTE_B7 3951 #define NOTE_C8 4186 #define NOTE_CS8 4435 #define NOTE_D8 4699 #define NOTE_DS8 4978 TimerSerial mySerial; int speakerPin; //   void setup() { speakerPin = 14; //  1.6 ( 14)      mySerial.begin(); mySerial.println("Welcome to CommitBeep 1.0"); pinMode(speakerPin, OUTPUT); } void playTone(int tone, int duration) { for (long i = 0; i < duration * 1000L; i += tone * 2) { digitalWrite(speakerPin, HIGH); delayMicroseconds(tone); digitalWrite(speakerPin, LOW); delayMicroseconds(tone); } digitalWrite(speakerPin, LOW); } //  void beep(int melody) { switch ((int)melody) { case '2': { int mel[] = {NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3}; int noteDurations[] = {4,8,8,4,4}; for (int thisNote = 0; thisNote < 4; thisNote++) { int noteDuration = 1000/noteDurations[thisNote]; playTone(mel[thisNote], noteDuration); } break; } case '3': { int mel[] = {NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3}; int noteDurations[] = {4,8,8,4,4}; for (int thisNote = 0; thisNote < 4; thisNote++) { int noteDuration = 1000/noteDurations[thisNote]; playTone(mel[thisNote], noteDuration); } break; } default: { int mel[] = {NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3}; int noteDurations[] = {4,8,8,4,4}; for (int thisNote = 0; thisNote < 4; thisNote++) { int noteDuration = 1000/noteDurations[thisNote]; playTone(mel[thisNote], noteDuration); } } } } //   void loop() { while (mySerial.available()) { char inChar = (char)mySerial.read(); beep(inChar); delay(100); } } 


We connect, fill, open the terminal window, write 1 there, and listen to the default melody.



Task Manager

Oddly enough, we will use the task scheduler for the background execution of our PHP script.

Let's write a CMD file that calls PHP and the script itself:
D:\denwer\usr\bin\php5.exe D:\denwer\home\test\www\commit_beep\index.php

And so that this economy does not callus eyes with a black console window, we will use the uncomplicated hidcon utility from Andrei Grechkin.

Connection Scheduler:



Finishing touches

By itself, everything works and squeaks hard at every commit. But on the table does not look very nice. It remains to wrap our new piece of metal in a nice plastic case, after working a bit with a clerical knife and tape:




Results

One day to develop from the idea to the finished device. Interesting and fascinating struggle with pitfalls and familiarity with Energia and MSP430.
Project budget ~ 200-300 rubles.

Sources on github .

Update: Added support for RTTTL - tunes in hard mode UART.
In order to enable the hard drive mode, you need to tap the jumpers RT / TX (Thanks BoxaShu ).
Sources updated.

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


All Articles