Timed 1 millisecond interrupt routine for Arduino.

September 1st, 2013

With Arduino (or AVR in general for that matter) you sometimes have the need to execute some code at regular intervals. A timed interrupt routine is the proper solution, but the Arduino IDE doesn’t come with standard code for this and it can be quite complicated for those who are unfamiliar with directly programming the hardware.

I created a simple proof of concept code that runs on AVR-type Arduino’s and uses a timed interrupt to time exact 1 ms, based on the 16MHz system clock. Thus 1000 counts equals 1 second with the same accuracy as the system clock. As proof the standard LED on pin 13 will start to blink in a 0.5Hz rhythm, one second on, one second off.

Timer0 and Timer1 are commonly used for the Arduino and third party libraries, so that leaves us with Timer2. Arduino’s system clock runs at 16MHz. Timer2 is clocked through a prescaler, this basically means that the system clock is first divided before it is fed to the Timer/Counter. I selected a 128 prescaler by configuring TCCR2B. This means the Timer/Counter is clocked at 16MHz / 128 = 125kHz.

Next I decided to make Timer/Counter2 compare to a set value 125 by setting OCR2A to 125 and configuring ‘Clear Timer on Compare’-bit in TCCR2A. This results in an exact 1kHz interrupt. TIMSK2 is configured to enable the Timer/Counter2 CompareA interrupt. Then all that is left is to attach the interrupt to some code, which is done with the ISR( TIMER2_COMPA_vect ) command. The Interrupt Service Routine counts to 1000 and so we arrive at 1 second ticks which can be used in the main loop to make the LED blink.

Removing spam from a Scuttle site

March 11th, 2013

I run this Scuttle based website to keep track of some of my favourite Electronics websites. Today I came home from work and found a huge amount of spam entries on the site and by the number of entries it was quickly decided that this was a good opportunity to polish up my MySQL skills. Luckily for me I haven’t put up too many links on that tag cloud lately and thus the spam was easily identified by a recent date.

I logged on to the Scuttle database and checked the tables:

mysql> show tables; +-------------------+ | Tables_in_scuttle | +-------------------+ | sc_bookmarks | | sc_tags | | sc_users | | sc_watched | +-------------------+ 4 rows in set (0.00 sec)

Quickly checking the website itself, it occured to me that there were two separate actions:

  • disable the users so they cannot log on any more to create new entries;
  • disable the spam entries so they are no longer visible.

Disable newly created users

First I had to check the names of the various columns in the sc_users table:

mysql> describe sc_users; +-----------+--------------+------+-----+---------------------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+--------------+------+-----+---------------------+----------------+ | uId | int(11) | NO | PRI | NULL | auto_increment | | username | varchar(25) | NO | | | | | password | varchar(40) | NO | | | | | uDatetime | datetime | NO | | 0000-00-00 00:00:00 | | | uModified | datetime | NO | | 0000-00-00 00:00:00 | | | name | varchar(50) | YES | | NULL | | | email | varchar(50) | NO | | | | | homepage | varchar(255) | YES | | NULL | | | uContent | text | YES | | NULL | | +-----------+--------------+------+-----+---------------------+----------------+ 9 rows in set (0.01 sec)

The dDatetime column appears to contain useful data to filter for recently added accounts, especially when filtering on todays year and month only:

mysql> select username,uDatetime from sc_users where uDatetime>'2013-03-'; +-----------------+---------------------+ | username | uDatetime | +-----------------+---------------------+ | clintayal | 2013-03-06 06:52:35 | | utyklaudi | 2013-03-08 09:14:29 | | jewellhue | 2013-03-08 10:48:06 | | wilburnva | 2013-03-08 12:09:12 | | reggieing | 2013-03-08 12:29:56 | | janinaant | 2013-03-08 13:39:46 | | shaunacha | 2013-03-08 14:17:29 | | joannehas | 2013-03-08 18:01:04 | | germanbow | 2013-03-08 18:04:52 | | hildegard | 2013-03-08 18:18:01 | | merediths | 2013-03-08 18:40:49 | | czilaurie | 2013-03-08 19:11:39 | | leoniepha | 2013-03-08 19:25:48 | | damiondra | 2013-03-08 19:36:11 | | lidiahear | 2013-03-08 19:56:02 | | terrygood | 2013-03-08 20:03:32 | | amelieover | 2013-03-08 20:04:36 | | peggymccl | 2013-03-08 20:42:12 | | hymannava | 2013-03-08 21:04:10 | | tammarazi | 2013-03-08 21:33:19 | | qsakelsey | 2013-03-08 22:05:36 | | lynellxgw | 2013-03-08 22:27:30 | | dillonii | 2013-03-08 22:29:12 | | doreencre | 2013-03-08 22:39:25 | | mavismadd | 2013-03-08 23:04:57 | | thomasbls | 2013-03-08 23:48:37 | | lawanna85 | 2013-03-08 23:49:45 | | harrietts | 2013-03-08 23:49:57 | | brent0 | 2013-03-09 00:12:41 | | aileenstra | 2013-03-09 01:17:36 | | trudybord | 2013-03-09 01:36:10 | | joycemalo | 2013-03-09 02:47:57 | | dqhwendi | 2013-03-09 04:59:44 | | keeshafor | 2013-03-09 05:11:50 | | priscilla | 2013-03-09 05:16:35 | | selenamul | 2013-03-09 06:24:06 | | darlaorr | 2013-03-09 07:23:42 | | geoffreye | 2013-03-09 08:08:04 | | corneliusr | 2013-03-09 08:08:37 | | heidiradf | 2013-03-09 09:04:38 | | charitymc | 2013-03-09 09:55:55 | | lorenalbr | 2013-03-09 10:35:37 | | edgarwest | 2013-03-09 10:41:40 | | junepelti | 2013-03-09 10:43:56 | | louienmn | 2013-03-09 10:47:06 | | youngball | 2013-03-09 10:55:50 | | gordonacu | 2013-03-09 11:12:29 | | melindast | 2013-03-09 11:32:30 | | rustylitt | 2013-03-09 11:38:02 | | bruce93i | 2013-03-09 12:21:06 | | dianagrif | 2013-03-09 13:41:05 | | angel5090 | 2013-03-09 14:48:41 | | clintonca | 2013-03-09 15:08:24 | | finlaypurv | 2013-03-09 15:21:27 | | xsksusann | 2013-03-09 17:16:47 | | rhodabris | 2013-03-09 18:27:16 | | joannepra | 2013-03-09 18:28:39 | | jamesrohd | 2013-03-09 18:28:47 | | roxanaste | 2013-03-09 18:29:04 | | mirtagdfa | 2013-03-09 18:29:27 | | fidelia49 | 2013-03-09 18:29:53 | | stephena7 | 2013-03-09 18:30:17 | | jeramytkk | 2013-03-09 18:31:39 | | lorenzouk | 2013-03-09 18:51:53 | | terrellnf | 2013-03-09 19:57:37 | | kitnmzwea | 2013-03-09 20:12:12 | | antoine06 | 2013-03-09 20:36:58 | | shelby230 | 2013-03-09 21:16:32 | | deliabarr | 2013-03-09 21:31:09 | | lavonnesh | 2013-03-09 22:04:28 | | andrawyli | 2013-03-09 23:37:55 | | noexpvfyvd | 2013-03-09 23:43:06 | | kareemwor | 2013-03-09 23:44:38 | | bradygand | 2013-03-09 23:47:18 | | joshmetz | 2013-03-10 00:04:26 | | bellcorey | 2013-03-10 00:09:35 | | debwensub | 2013-03-10 00:22:36 | | nathanielm | 2013-03-10 01:02:43 | | justinadu | 2013-03-10 01:40:35 | | isiahsmxh | 2013-03-10 01:46:01 | | hazelmcca | 2013-03-10 01:49:06 | | oliviarit | 2013-03-10 03:20:05 | | matthewbe | 2013-03-10 03:48:28 | | monicaluc | 2013-03-10 04:17:48 | | elissasee | 2013-03-10 05:16:58 | | chadwickce | 2013-03-10 06:23:55 | | billv | 2013-03-10 06:28:12 | | michelsch | 2013-03-10 06:48:55 | | christyca | 2013-03-10 08:24:38 | | rebbecach | 2013-03-10 09:34:49 | | oscarsaen | 2013-03-10 11:39:14 | | marvinwof | 2013-03-10 11:53:01 | | andersonm | 2013-03-10 12:00:31 | | juliusgra | 2013-03-10 12:10:01 | | lance47f | 2013-03-10 12:16:59 | | dennisgle | 2013-03-10 12:41:50 | | hattiel20 | 2013-03-10 12:51:12 | | christyth | 2013-03-10 12:52:14 | | sylvester | 2013-03-10 12:55:44 | | allanzof | 2013-03-10 13:38:27 | | florianoe | 2013-03-10 13:43:24 | | spencer3h | 2013-03-10 13:50:19 | | brendasug | 2013-03-10 17:52:22 | | ameesampl | 2013-03-10 18:31:57 | | lawannahw | 2013-03-10 21:06:43 | | dannyburt | 2013-03-10 21:21:38 | | deloraswhi | 2013-03-10 21:29:38 | | jesusnall | 2013-03-10 21:46:50 | | lindseymc | 2013-03-10 22:18:38 | | leannebil | 2013-03-10 22:28:31 | | benedictd | 2013-03-10 22:30:50 | | candice52 | 2013-03-10 22:42:05 | | vfrzack | 2013-03-10 23:02:52 | | fletcherp | 2013-03-10 23:25:47 | | alvaroozw | 2013-03-10 23:26:54 | | raleighb6 | 2013-03-10 23:39:07 | | deliawolc | 2013-03-10 23:56:07 | | lxxlashon | 2013-03-11 00:14:09 | | ginohorto | 2013-03-11 00:44:24 | | fayohault | 2013-03-11 00:44:26 | | nildat42 | 2013-03-11 01:04:07 | | concetta4 | 2013-03-11 01:04:40 | | suzannel53 | 2013-03-11 01:58:14 | | ginaearls | 2013-03-11 02:18:26 | | lucindalem | 2013-03-11 03:09:11 | | indiradurr | 2013-03-11 03:48:32 | | valentina6 | 2013-03-11 05:00:02 | | ckpveroni | 2013-03-11 05:40:50 | | marianamsi | 2013-03-11 05:53:09 | | torymorel | 2013-03-11 05:56:50 | | carolekke | 2013-03-11 06:06:29 | | devinburdzil80 | 2013-03-11 06:07:34 | | williemaef | 2013-03-11 06:23:38 | | jeannejac | 2013-03-11 06:56:22 | | emanuelxdv | 2013-03-11 07:19:57 | | luisdugan | 2013-03-11 07:37:53 | | maxwellgu | 2013-03-11 07:48:16 | | hershelcl | 2013-03-11 08:06:20 | | lavadapar | 2013-03-11 08:14:31 | | augustus1 | 2013-03-11 08:49:51 | | jamaalven | 2013-03-11 10:17:59 | | katherina | 2013-03-11 10:25:47 | | lorettama | 2013-03-11 10:25:53 | | gxatricia | 2013-03-11 10:48:09 | | garfieldqv | 2013-03-11 10:51:50 | | carlosauc | 2013-03-11 12:27:08 | | estelab04 | 2013-03-11 12:44:28 | | libbyt10 | 2013-03-11 12:59:03 | | mablewhit | 2013-03-11 13:10:56 | | groveraoe | 2013-03-11 13:18:19 | | normahiat | 2013-03-11 14:27:39 | | kateuo | 2013-03-11 14:53:27 | | gerardogu | 2013-03-11 15:33:09 | | lyndonlan | 2013-03-11 16:12:20 | | jefferyka | 2013-03-11 16:36:57 | | merrillkallas47 | 2013-03-11 17:28:59 | | willabeau | 2013-03-11 17:39:37 | | yettaholli | 2013-03-11 18:01:56 | | irvin00h | 2013-03-11 18:32:21 | | stevecaste | 2013-03-11 18:40:44 | | brucedaf | 2013-03-11 18:44:21 | | martinaxba | 2013-03-11 19:02:42 | +-----------------+---------------------+ 162 rows in set, 1 warning (0.00 sec)

A while ago already I had figured out that setting the password column to -1 was sufficient to keep the user from logging in again. Since there were 162 new users created in the past few days I figured I had to execute a smart MySQL statement instead of clicking around in phpmyadmin:

mysql> update sc_users set password=-1 where uDatetime>'2013-03-'; Query OK, 162 rows affected, 1 warning (0.01 sec) Rows matched: 162 Changed: 162 Warnings: 1

Removing the spam

Next step was to remove the actual spam from the website. The bookmarks were also created during this month and again filtering was pretty easy. First I had to check the columns in the related table:

mysql> describe sc_bookmarks; +--------------+--------------+------+-----+---------------------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+--------------+------+-----+---------------------+----------------+ | bId | int(11) | NO | PRI | NULL | auto_increment | | uId | int(11) | NO | MUL | 0 | | | bIp | varchar(40) | YES | | NULL | | | bStatus | tinyint(1) | NO | | 0 | | | bDatetime | datetime | NO | MUL | 0000-00-00 00:00:00 | | | bModified | datetime | NO | | 0000-00-00 00:00:00 | | | bTitle | varchar(255) | NO | | | | | bAddress | text | NO | | NULL | | | bDescription | varchar(255) | YES | | NULL | | | bHash | varchar(32) | NO | MUL | | | +--------------+--------------+------+-----+---------------------+----------------+ 10 rows in set (0.00 sec)

To double check the selection that I was about to disable, I checked it with a select statement:

mysql> select bIp,bDatetime from sc_bookmarks where bDatetime>'2013-03-'; +-----------------+---------------------+ | bIp | bDatetime | +-----------------+---------------------+ | 194.71.223.204 | 2013-03-08 12:30:09 | | 94.23.180.200 | 2013-03-06 06:52:37 | | 219.234.82.86 | 2013-03-08 09:14:37 | | 198.143.144.148 | 2013-03-08 10:48:09 | | 192.119.148.228 | 2013-03-08 12:09:18 | | 50.31.107.54 | 2013-03-08 13:40:27 | | 147.255.162.169 | 2013-03-08 14:17:34 | | 173.0.51.155 | 2013-03-08 18:01:09 | | 173.0.51.155 | 2013-03-08 18:05:01 | | 173.234.196.153 | 2013-03-08 18:18:14 | | 50.115.168.164 | 2013-03-08 18:41:00 | | 198.143.145.198 | 2013-03-08 19:11:40 | | 173.0.51.155 | 2013-03-08 19:36:14 | | 173.0.51.155 | 2013-03-08 19:38:04 | | 147.255.162.207 | 2013-03-08 19:56:11 | | 46.105.189.183 | 2013-03-08 20:04:40 | | 69.197.190.56 | 2013-03-08 20:42:22 | | 147.255.162.124 | 2013-03-08 21:04:16 | | 173.234.196.153 | 2013-03-08 21:35:58 | | 200.8.218.78 | 2013-03-08 22:06:09 | | 5.34.241.70 | 2013-03-08 22:27:38 | | 184.154.90.74 | 2013-03-08 22:29:54 | | 125.39.68.130 | 2013-03-08 23:05:00 | | 188.40.253.69 | 2013-03-08 23:48:47 | | 188.40.253.69 | 2013-03-08 23:49:53 | | 173.213.99.153 | 2013-03-08 23:50:05 | | 198.167.136.235 | 2013-03-09 00:13:24 | | 151.237.188.73 | 2013-03-09 01:18:19 | | 66.219.27.158 | 2013-03-09 01:36:12 | | 81.17.30.179 | 2013-03-09 02:48:12 | | 194.71.224.237 | 2013-03-09 04:59:47 | | 147.255.162.207 | 2013-03-09 05:16:40 | | 109.175.8.42 | 2013-03-09 06:26:57 | | 147.255.162.207 | 2013-03-09 07:23:49 | | 96.127.137.252 | 2013-03-09 08:08:17 | | 46.4.241.85 | 2013-03-09 09:04:48 | | 108.178.5.12 | 2013-03-09 09:56:01 | | 200.121.15.112 | 2013-03-09 10:36:06 | | 198.143.159.110 | 2013-03-09 10:42:02 | | 198.143.159.110 | 2013-03-09 10:43:59 | | 198.143.159.110 | 2013-03-09 10:47:07 | | 173.0.50.13 | 2013-03-09 10:55:51 | | 94.242.241.204 | 2013-03-09 11:12:29 | | 220.134.184.161 | 2013-03-09 11:32:35 | | 220.134.184.161 | 2013-03-09 11:38:08 | | 91.120.127.127 | 2013-03-09 12:21:29 | | 184.82.222.218 | 2013-03-09 13:41:05 | | 173.234.196.193 | 2013-03-09 14:48:56 | | 200.151.208.58 | 2013-03-09 15:09:50 | | 184.154.255.58 | 2013-03-09 15:21:41 | | 37.203.214.129 | 2013-03-09 17:16:48 | | 37.59.69.68 | 2013-03-09 18:27:25 | | 37.59.69.68 | 2013-03-09 18:28:43 | | 37.59.69.68 | 2013-03-09 18:28:51 | | 37.59.69.68 | 2013-03-09 18:29:08 | | 37.59.69.68 | 2013-03-09 18:29:30 | | 37.59.69.68 | 2013-03-09 18:29:56 | | 37.59.69.68 | 2013-03-09 18:30:22 | | 37.59.69.68 | 2013-03-09 18:31:43 | | 123.125.116.241 | 2013-03-09 18:52:15 | | 46.227.71.177 | 2013-03-09 19:58:19 | | 219.234.82.55 | 2013-03-09 20:12:20 | | 219.234.82.72 | 2013-03-09 20:37:13 | | 194.71.222.103 | 2013-03-09 21:16:37 | | 219.234.82.76 | 2013-03-09 21:31:20 | | 219.234.82.53 | 2013-03-09 22:04:35 | | 123.125.116.242 | 2013-03-09 23:38:01 | | 66.248.200.17 | 2013-03-09 23:43:19 | | 66.248.193.131 | 2013-03-09 23:45:08 | | 64.120.239.132 | 2013-03-09 23:47:23 | | 219.234.82.81 | 2013-03-10 00:04:34 | | 173.234.57.198 | 2013-03-10 00:09:37 | | 219.234.82.72 | 2013-03-10 00:22:50 | | 198.143.165.198 | 2013-03-10 01:03:05 | | 188.136.216.130 | 2013-03-10 01:46:08 | | 188.136.216.130 | 2013-03-10 01:49:13 | | 219.234.82.88 | 2013-03-10 03:13:30 | | 82.200.254.138 | 2013-03-10 03:20:46 | | 46.227.70.244 | 2013-03-10 03:48:32 | | 219.234.82.83 | 2013-03-10 04:17:53 | | 219.234.82.83 | 2013-03-10 05:17:17 | | 91.108.177.186 | 2013-03-10 06:24:00 | | 96.127.137.250 | 2013-03-10 06:28:57 | | 173.0.51.225 | 2013-03-10 06:52:57 | | 173.0.51.225 | 2013-03-10 06:53:21 | | 173.0.51.225 | 2013-03-10 07:38:12 | | 173.0.51.225 | 2013-03-10 07:49:57 | | 173.0.51.225 | 2013-03-10 07:57:38 | | 173.234.196.251 | 2013-03-10 08:14:25 | | 66.248.202.146 | 2013-03-10 08:24:41 | | 69.197.190.56 | 2013-03-10 08:35:29 | | 173.234.196.251 | 2013-03-10 09:35:01 | | 147.255.162.177 | 2013-03-10 11:16:37 | | 69.175.97.76 | 2013-03-10 11:39:16 | | 173.234.196.83 | 2013-03-10 11:44:06 | | 5.135.11.113 | 2013-03-10 11:53:01 | | 219.234.82.84 | 2013-03-10 12:00:45 | | 219.234.82.73 | 2013-03-10 12:10:09 | | 66.248.193.180 | 2013-03-10 12:17:02 | | 173.234.196.153 | 2013-03-10 12:20:58 | | 66.248.193.251 | 2013-03-10 12:41:53 | | 192.119.154.206 | 2013-03-10 12:51:14 | | 192.119.154.206 | 2013-03-10 12:52:17 | | 192.119.154.206 | 2013-03-10 12:55:47 | | 69.175.25.2 | 2013-03-10 13:38:30 | | 186.219.25.226 | 2013-03-10 13:47:43 | | 37.72.190.105 | 2013-03-10 13:51:14 | | 219.234.82.55 | 2013-03-10 17:52:30 | | 69.175.29.69 | 2013-03-10 18:32:03 | | 173.0.51.141 | 2013-03-10 21:06:51 | | 69.175.112.21 | 2013-03-10 21:21:44 | | 91.108.177.187 | 2013-03-10 21:31:23 | | 173.234.196.193 | 2013-03-10 21:39:41 | | 5.135.4.16 | 2013-03-10 21:46:54 | | 192.119.151.232 | 2013-03-10 22:18:54 | | 66.248.193.171 | 2013-03-10 22:28:38 | | 5.144.176.5 | 2013-03-10 22:30:52 | | 88.80.21.218 | 2013-03-10 22:42:06 | | 219.234.82.82 | 2013-03-10 23:03:18 | | 195.190.13.117 | 2013-03-10 23:25:47 | | 178.33.83.66 | 2013-03-10 23:27:00 | | 176.31.3.242 | 2013-03-10 23:39:09 | | 192.119.148.223 | 2013-03-10 23:56:13 | | 197.220.163.75 | 2013-03-11 00:14:17 | | 147.255.162.177 | 2013-03-11 00:16:59 | | 94.23.118.183 | 2013-03-11 00:44:30 | | 199.192.205.77 | 2013-03-11 00:44:30 | | 96.127.137.250 | 2013-03-11 01:04:24 | | 91.108.177.170 | 2013-03-11 01:04:48 | | 147.255.162.207 | 2013-03-11 01:26:18 | | 69.175.58.62 | 2013-03-11 01:58:20 | | 69.175.97.75 | 2013-03-11 02:18:29 | | 66.248.193.118 | 2013-03-11 03:09:16 | | 147.255.162.195 | 2013-03-11 03:38:00 | | 66.248.194.60 | 2013-03-11 03:48:38 | | 199.101.102.24 | 2013-03-11 04:09:24 | | 66.248.193.50 | 2013-03-11 05:00:07 | | 199.119.227.220 | 2013-03-11 05:40:53 | | 66.248.194.60 | 2013-03-11 05:53:18 | | 110.77.213.186 | 2013-03-11 05:58:46 | | 110.77.213.186 | 2013-03-11 06:06:33 | | 109.169.73.116 | 2013-03-11 06:07:34 | | 69.175.112.21 | 2013-03-11 06:23:44 | | 74.221.208.145 | 2013-03-11 06:56:27 | | 184.154.174.10 | 2013-03-11 07:20:04 | | 37.72.190.105 | 2013-03-11 07:38:08 | | 88.80.20.191 | 2013-03-11 07:48:17 | | 5.144.176.11 | 2013-03-11 08:06:28 | | 192.119.144.219 | 2013-03-11 08:14:36 | | 78.46.70.86 | 2013-03-11 08:49:55 | | 178.73.202.147 | 2013-03-11 10:18:00 | | 125.110.223.248 | 2013-03-11 10:25:49 | | 125.110.223.248 | 2013-03-11 10:25:54 | | 216.151.139.241 | 2013-03-11 10:48:11 | | 199.116.113.236 | 2013-03-11 10:51:59 | | 5.34.241.69 | 2013-03-11 10:59:29 | | 5.135.53.225 | 2013-03-11 12:27:13 | | 88.80.21.98 | 2013-03-11 12:44:45 | | 192.119.144.202 | 2013-03-11 12:59:09 | | 192.119.148.190 | 2013-03-11 13:11:01 | | 194.71.223.57 | 2013-03-11 13:18:25 | | 88.80.21.92 | 2013-03-11 14:28:24 | | 173.236.40.142 | 2013-03-11 14:53:46 | | 119.191.60.150 | 2013-03-11 15:33:46 | | 50.115.173.137 | 2013-03-11 15:37:47 | | 5.135.53.232 | 2013-03-11 16:12:27 | | 173.234.196.251 | 2013-03-11 16:19:13 | | 89.237.134.10 | 2013-03-11 16:37:00 | | 173.234.196.153 | 2013-03-11 17:14:10 | | 109.169.73.116 | 2013-03-11 17:29:00 | | 222.218.152.92 | 2013-03-11 17:40:36 | | 184.154.254.174 | 2013-03-11 18:02:08 | | 80.191.78.251 | 2013-03-11 18:32:34 | | 66.248.193.118 | 2013-03-11 18:40:52 | | 173.213.96.64 | 2013-03-11 18:44:26 | | 173.236.35.146 | 2013-03-11 19:02:55 | | 198.167.136.240 | 2013-03-11 19:36:28 | +-----------------+---------------------+ 177 rows in set, 2 warnings (0.00 sec)

To finally actually disable the bookmarks with a similar trick as disabling the users: putting -1 to the bId column:

mysql> update sc_bookmarks set bStatus=-1 where bDatetime>'2013-03-'; Query OK, 177 rows affected, 3 warnings (0.01 sec) Rows matched: 177 Changed: 177 Warnings: 3

Adding the IP-addresses to the firewall

To prevent these misbehaving IP-addresses to deface my webserver again, I put up a iptables firewall to easily block them with an ipset command. It is outside the scope of this article to explain the iptables firewall, but this is the related line in iptables:

-A INPUT -m set --match-set brute_force src -m comment --comment "misbehaving IPs" -j DROP

And this is how I inserted the IP-addesses all at once from the Linux command line:

mysql -username -password database -s -e \ "select distinct bIp from sc_bookmarks where bDatetime>'2013-03-';" | while read ip; do sudo /usr/sbin/ipset --add brute_force $ip; done

It feels good for once to be able to remove spam more quickly than it took them to put it up there.

udev file for Texas Instruments LaunchPads

February 2nd, 2013

To be able to use the various devices on Linux as an unprivileged (non-root) user, it is required to configure udev in such a way that appropriate authorizations are assigned to it upon plugging in the device.

For both the MSP430 and the Stellaris LaunchPad I’ve developed a simple but safe udev rules file, based on its configured serial number. This means that even when more than one LaunchPad is attached to the system, it will get a unique identifier that survives reboots. Every LaunchPad supplied by TI has a unique serial number configured upon delivery.

Retrieving the serial number

To be able to create a unique device name that survives a reboot, it is essential to retrieve the serial number of the device. This can easily be done by following these instructions:

  1. Detach the LaunchPad from the system;
  2. On the command line type (this requires the expect-dev package being installed):
     Bash |  copy code |? 
    unbuffer udevadm monitor --environment | grep 'ID_SERIAL='
  3. Plug in the LaunchPad. Notice that MSP430 takes about 10 seconds to fully register with the system.
  4. Output similar like this is displayed for MSP430:
    ID_SERIAL=Texas_Instruments_Texas_Instruments_MSP-FET430UIF_36FF49ABB1D22050
  5. Output similar like this is displayed for Stellaris:
    ID_SERIAL=Texas_Instruments_In-Circuit_Debug_Interface_0E10A714

Creating the udev rules file

With this knowledge we can build the the udev rules file:

 Text |  copy code |? 
  1. ACTION!="add", GOTO="persistent_serial_end"
  2. SUBSYSTEM!="tty", GOTO="persistent_serial_end"
  3. KERNEL!="ttyACM[0−9]*", GOTO="persistent_serial_end"
  4. IMPORT{builtin}="usb_id"
  5. ENV{ID_SERIAL}=="Texas_Instruments_In−Circuit_Debug_Interface_0E10A714"               ,  SYMLINK="stellaris−001"  ,  OWNER="jhendrix"  ,  MODE:="0600"
  6. ENV{ID_SERIAL}=="Texas_Instruments_Texas_Instruments_MSP−FET430UIF_36FF49ABB1D22050"  ,  SYMLINK="msp430−001"     ,  OWNER="jhendrix"  ,  MODE:="0600"
  7. LABEL="persistent_serial_end"

  1. Edit lines 6 and/or 7 to contain the serial number found in the previous paragraph. Notice that only the part between double quotes needs to be edited. Of course you need as many lines as you own LaunchPads. I currently own two, so I have two lines.
  2. Where it says “jhendrix“, change that to your own username under which you will be working with the device.
  3. Save the file as /etc/udev/rules.d/60-ttyACM.rules

Using the the rules file

  1. Reload udev:
     Bash |  copy code |? 
    sudo udevadm control --reload
  2. Unplug the LaunchPad;
  3. Plug the LaunchPad and wait a couple of seconds;
  4. Check the device file:
    1. ls -l /dev | grep ttyACM
    2. lrwxrwxrwx  1 root     root             7 Feb  2 21:57 msp430-001 -> ttyACM1
    3. lrwxrwxrwx  1 root     root             7 Feb  2 21:23 stellaris-001 -> ttyACM0
    4. crw-------  1 jhendrix dialout   166,   0 Feb  2 21:23 ttyACM0
    5. crw-------  1 jhendrix plugdev   166,   1 Feb  2 21:57 ttyACM1
  5. From this moment onward you can use the Launchpad under any of the devices listed:
    1. msp430-001 and stellaris-001 will survive a reboot;
    2. ttyACM0 and ttyACM1 are dynamic, they change according of attaching the devices.
  6. If the demo program is loaded that Stellaris comes with, you can access it from now on by typing:
     Bash |  copy code |? 
    screen /dev/stellaris-001 115200

TI MSP430 LaunchPad Temperature Demo Application

January 19th, 2013

 

When you receive a new Texas Instruments MSP430 LaunchPad, it comes with a small demo application installed so you can easily check its functionality. The interesting thing is that it also interfaces with the PC through a virtual serial interface, but reading the data from a Linux PC appears to be non-trivial. This is partially caused by the fact that the user’s guide states that the demo application can be read with any serial console application, which in practice just doesn’t always work.

Attaching the LaunchPad

When an MSP430 LaunchPad is attached to the system, dmesg logs a few lines similar like these:

  1. [19:52:40] usb 2-2.3: new full-speed USB device number 85 using ehci_hcd
  2. [19:52:40] usb 2-2.3: New USB device found, idVendor=0451, idProduct=f432
  3. [19:52:40] usb 2-2.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  4. [19:52:40] usb 2-2.3: Product: Texas Instruments MSP-FET430UIF
  5. [19:52:40] usb 2-2.3: Manufacturer: Texas Instruments
  6. [19:52:40] usb 2-2.3: SerialNumber: 36FF49ABB1D22050
  7. [19:52:40] cdc_acm 2-2.3:1.0: This device cannot do calls on its own. It is not a modem.
  8. [19:52:40] cdc_acm 2-2.3:1.0: No union descriptor, testing for castrated device
  9. [19:52:40] cdc_acm 2-2.3:1.0: ttyACM0: USB ACM device
  10. [19:52:50] hid-generic 0003:0451:F432.003E: usb_submit_urb(ctrl) failed: -1
  11. [19:52:50] hid-generic 0003:0451:F432.003E: timeout initializing reports
  12. [19:52:50] hid-generic 0003:0451:F432.003E: hiddev0,hidraw3: USB HID v1.01 Device [Texas Instruments Texas Instruments MSP-FET430UIF] on usb-0000:00:1d.7-2.3/input1

According to line 9 of this logging, in this case the LaunchPad is attached to the system as ‘/dev/ttyACM0′.

Reading the data from MSP430 LaunchPad

For me only minicom (2400 8N1) was showing some data, but in contrast to what I understood from the user’s guide the received data is binary, not in ASCII.  I wrote a quick and dirty Perl script that easily allows for reading the data.

After attaching the LaunchPad and pressing button P1.3 on it, it starts to send data several times per second. Running the script below from the command line, outputs the current temperature.

Receiving serial data ... 22.2C 22.2C 22.8C 22.8C 22.8C 22.2C 22.8C 22.8C 22.2C 22.8C 22.8C 22.8C 22.2C 22.8C 22.8C 22.8C 22.8C 22.8C 22.8C 22.8C 22.8C 22.2C 22.2C 22.8C

The script is no rocket science, its main goal is to prove the LaunchPad is working properly and to act as a starting point for others.

 Perl |  copy code |? 
  1. #!/usr/bin/perl
  2. use warnings;
  3. use strict;
  4. use Device::SerialPort;
  5. use Time::HiRes qw( usleep );
  6. my $PortName = '/dev/ttyACM0';
  7. my $PortObj;
  8. $| = 1;
  9. print STDERR "Waiting for serial port ...\n";
  10. while ( not( $PortObj = new Device::SerialPort ($PortName, 'false' ) ) ) {
  11.         print STDERR "Waiting: Cannot open serial port: $!\n";
  12.         sleep( 1 );
  13. }
  14. $PortObj->databits( 8 );
  15. $PortObj->baudrate( 2400 );
  16. $PortObj->parity( "none" );
  17. $PortObj->stopbits( 1 );
  18. $PortObj->handshake( "none" );
  19. print STDERR "Receiving serial data ...\n";
  20. my $newline = 8;
  21. while ( 1 ) {
  22.         if ( not $newline ) {
  23.                 # print 8 values per line
  24.                 print "\n";
  25.                 $newline = 8;
  26.         }
  27.         # try to read a byte of data
  28.         my ( $count, $data ) = $PortObj->read( 1 );
  29.         if ( ( defined( $count ) ) and ( $count != 0 ) ) {
  30.                 # if data available, then convert from Fahrenheit to Celcius
  31.                 printf( "%3.1fC   " , ( ( ord( $data ) - 32 ) * 5 / 9 ) );
  32.                 $newline--;
  33.         }
  34.         usleep( 100000 );       # wait 100ms
  35. }

TI MSP430 LaunchPad from the Linux Command Line

January 19th, 2013

Installing required packages

 Bash |  copy code |? 
$ sudo apt-get install binutils-msp430 gcc-msp430 gdb-msp430 msp430-libc msp430mcu mspdebug

Debugger

mspdebug home page

 Bash |  copy code |? 
$ mspdebug rf2500

MSPDebug version 0.19 - debugging tool for MSP430 MCUs Copyright (C) 2009-2012 Daniel Beer <dlbeer@gmail.com> This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Trying to open interface 1 on 050 rf2500: warning: can't detach kernel driver: No data available Initializing FET... FET protocol version is 30394216 Configured for Spy-Bi-Wire Set Vcc: 3000 mV fet: FET returned error code 4 (Could not find device (or device not supported)) fet: command C_IDENT1 failed fet: identify failed Trying again... Initializing FET... FET protocol version is 30394216 Configured for Spy-Bi-Wire Sending reset... Set Vcc: 3000 mV Device ID: 0x2553 Device: MSP430G2553 Code memory starts at 0xc000 Number of breakpoints: 2 Available commands: = delbreak gdb load opt reset simio alias dis help locka prog run step break erase hexout md read set sym cgraph exit isearch mw regs setbreak Available options: color gdb_loop iradix fet_block_size gdbc_xfer_size quiet Type "help <topic>" for more information. Press Ctrl+D to quit. (mspdebug) step ( PC: 0fcc6) ( R4: 09b66) ( R8: 0ffdf) (R12: 00000) ( SP: 0027e) ( R5: 0c7ff) ( R9: 0ff65) (R13: 0fd90) ( SR: 00000) ( R6: 0defc) (R10: 0e7ff) (R14: 00000) ( R3: 00000) ( R7: 0e157) (R11: 00298) (R15: 0ffff) 0xfcc6: 0fcc6: b2 40 6e fd 36 02 MOV #0xfd6e, &0x0236 0fccc: b2 40 6e fd 38 02 MOV #0xfd6e, &0x0238 0fcd2: b0 12 66 fd CALL #0xfd66

Driving the Adafruit mshield v1.0 from Bitlash

January 5th, 2013

Bitlash is a brilliant and powerful sketch for Arduino to do your ad hoc interactive bit banging on attached hardware. Once loaded on an Arduino, you can simply connect to it with a serial terminal emulator and start typing commands.

So yesterday I decided to find out exactly how powerful Bitlash is by bit banging an Adafruit motorshield (v1.0) for a single bipolar stepper motor. The power is in the fact that Bitlash allows you to define functions that are subsequently stored in EEPROM and so you can figure out bit by bit how to control the shield. Now I’m not going through the whole step by step procedure of reverse engineering out how the shield is wired, as the resulting Bitlash code turned out pretty straightforward and there are circuit diagrams for that.

First various Arduino output pins are to be initialized as output:

function initstepper {pinmode(3,1);pinmode(4,1);pinmode(7,1);pinmode(8,1);pinmode(11,1);pinmode(12,1);d3=1;d7=0;d11=1};

  • pin 3 → enable M2
  • pin 4 → serial clock (rising edge)
  • pin 7 → shift register not(Output Enable)
  • pin 8 → serial data
  • pin 11 → enable M1
  • pin 12 → shift register latch

To save digital pins, the motor pins are attached through a shift register. This implies that motor outputs can only be changed by shifting bits into the register and latching it to its outputs. The following functions are called under water:

function bo {d4=0;d8=arg(1);d4=1;}; function latch {d12=0;d12=1;}; function dataout {bo(arg(1));bo(arg(2));bo(arg(3));bo(arg(4));bo(arg(5));bo(arg(6));bo(arg(7));bo(arg(8));latch};

  • bo → shift a single bit into the shift register
  • latch → copy the shift register contents to its output register
  • dataout → shift 8 bits data into the shift register, then latch it to its outputs

Now the tricky bit is how the bits in the data are related to the motor pins. Here is a table:

M1 x x M2 x x bit 0 1 2 3 4 5 6 7 M3 x x M4 x x

The last step is to define the separate steps for the stepper motor. For a full step, eight substeps can be identified:

b5 b4 b3 b6 M1 M2 M1a M1b M2a M2b s1 + 0 1 1 1 s2 + + 0 1 0 1 s3 + 0 0 0 1 s4 - + 1 0 0 1 s5 - 1 0 0 0 s6 - - 1 0 1 0 s7 - 1 1 1 0 s8 + - 0 1 1 0

And code that into Bitlash functions:

function s1 {dataout(0,0,0,1,1,0,1,0);}; function s2 {dataout(0,0,0,0,1,0,1,0);}; function s3 {dataout(0,0,0,0,0,0,1,0);}; function s4 {dataout(0,0,0,0,0,1,1,0);}; function s5 {dataout(0,0,0,0,0,1,0,0);}; function s6 {dataout(0,0,0,1,0,1,0,0);}; function s7 {dataout(0,0,0,1,1,1,0,0);}; function s8 {dataout(0,0,0,1,1,0,0,0);};

To run the stepper motor:

function runstepper {while (1) { s1;s2;s3;s4;s5;s6;s7;s8}<!--DVFMTSC-->;}; initstepper runstepper

As a bonus it is pretty simple to change the stepper´s torque. The easiest way is to PWM the shift register’s output. The function below takes a single argument (0-255).

function torque {a7=255-arg(1);};

This will however affect all four half bridges on the shield. A prettier, though lightly more complex method is to PWM the enable M1 and M2 pins (resp. 11 and 3). This allows for microstepping, but the Bitlash method is really too slow for that.

Taking a screenshot from a virtual console

December 15th, 2012

Every once in a while it happens to me that while working on one of the 6 virtual consoles an error message pops up that I want to look up on Internet. But copy-paste between a virtual console and the desktop doesn’t work, let alone being able to select some text on the console with your mouse.

As long as the text is on screen in the virtual console, it is relatively simple to grab it from an xterm in the desktop with the command below. Just replace the number `2` with whichever console number you want to grab:

$ sudo setterm -dump 2 -file /dev/stdout Ubuntu 12.10 diablo tty2 diablo login: jhendrix Password: Last login: Sat Dec 15 09:26:02 CET 2012 on tty1 Welcome to Ubuntu 12.10 (GNU/Linux 3.5.0-17-generic x86_64) * Documentation: https://help.ubuntu.com/ *** System restart required *** No mail. jhendrix@diablo:~$

Simple rotary encoder controlled PWM dimmer

December 14th, 2012

This post is basically a proof of concept for how to use a rotary (quadrature) encoder with an Arduino. Rotary encoders look a bit like classic potentiometers, but instead of changing resistance between its pins, it has two outputs with digital signals 90 degrees shifted with respect to each other. Because of this 90° phase difference between its outputs, it is relatively simple to determine in which direction it was turned as at any given time only one of its outputs will change. An example of the output sequence is given in the table below.

       Left   Right
Step   B A    B A
 a     1 1    1 1
 b     1 0    0 1 
 c     0 0    0 0
 d     0 1    1 0
 a     1 1    1 1
 ...

There are two ways of implementing a rotary encoder in an Arduino sketch. The first one simply polls the state of the input pins and checks the current state with the previous state. The other option is to use one encoder output as interrupt trigger and the other output to indicate direction.

Using pin state polling

In this example Arduino’s inputs are continuously polled and stored in currentState. When currentState is different from the previousState polled, then the rotary encoder has changed its position and the PWM value has to be updated. The if-then construct (lines 37-41 and 45-49) is used to decide whether the change implies an increase or a decrease. Then a second if-then (lines 42 and 50) prevents PWM from changing from 100% to 0% (and the other way around) by passing end of scale.

The code:

 C++ |  copy code |? 
  1. /*
  2. This sketch implements a simple dimmer using a rotary encoder. The
  3. encoder is connected to pins 22 and 23.
  4. The standard led on pin 13 will be dimmed using pwm.
  5. More on: https://blog.linformatronics.nl/58/electronics/simple-rotary-encoder-controlled-pwm-dimmer
  6. */
  7. // Arduino Mega1280
  8. const uint8_t pwmPin = 13;
  9. const uint8_t encoderPinA = 22;   // encoder input channel A
  10. const uint8_t encoderPinB = 23;   // encoder input channel B
  11. uint8_t previousState = 0;
  12. uint8_t analogValue = 128;        // initialize at 50% PWM
  13. const uint8_t stepSize = 2;       // step size to increase PWM setting
  14. void setup(){
  15.   analogWrite( pwmPin , analogValue );
  16.   pinMode( pwmPin , OUTPUT );
  17.   pinMode( encoderPinA , INPUT_PULLUP );
  18.   pinMode( encoderPinB , INPUT_PULLUP );
  19. }
  20. void loop() {
  21.   uint8_t currentState = ( digitalRead( encoderPinA ) << 0 ) | ( digitalRead( encoderPinB ) << 1 );
  22.   
  23.   /*
  24.     State
  25.      B      A
  26.     red  yellow Digital
  27.      1      1      3
  28.      1      0      2
  29.      0      0      0
  30.      0      1      1
  31.   */
  32.   
  33.   if (
  34.       ( ( previousState == 3 ) & ( currentState == 2 ) ) |
  35.       ( ( previousState == 2 ) & ( currentState == 0 ) ) |
  36.       ( ( previousState == 0 ) & ( currentState == 1 ) ) |
  37.       ( ( previousState == 1 ) & ( currentState == 3 ) ) ) {
  38.     if ( analogValue <= ( 255 - stepSize ) ) {
  39.       analogValue += stepSize;
  40.     }
  41.   } else if (
  42.       ( ( previousState == 1 ) & ( currentState == 0 ) ) |
  43.       ( ( previousState == 0 ) & ( currentState == 2 ) ) |
  44.       ( ( previousState == 2 ) & ( currentState == 3 ) ) |
  45.       ( ( previousState == 3 ) & ( currentState == 1 ) ) ) {
  46.     if ( analogValue >= stepSize ) {
  47.       analogValue -= stepSize;
  48.     }
  49.   }
  50.   if ( previousState != currentState ) {
  51.     analogWrite( pwmPin , analogValue );
  52.     previousState = currentState;
  53.   }
  54. }

It is fairly easy to adapt this code for any other Arduino board types. It doesn’t rely on any specific hardware, just requires two digital input pins and one pin that can drive PWM.  Just change encoderPinA and encoderPinB. Most Arduino boards have an on board LED on pin 13 which is used for PWM here.

Using interrupts

The second option to implement a rotary encoder is using interrupts. An interrupt can be used to temporarily stop execution of the main program to service an event (interrupt), after which the main program is being resumed. The trick used here is to use one of the encoder outputs as a clock event (both rising and falling edges) attached to the interrupt input and the other input as direction indicator.

All the main loop does in this example is setting PWM value from analogValue, where the interrupt routine increases/decreases this value. This only works because of the fact that both edges (rising and falling) can be used to trigger the routine. Whenever the interrupt routine is entered, both encoder inputs are read. When both inputs are different (LOW, HIGH) the analogValue is increased, if both inputs are identical the analogValue is decreased.

The interrupt driven version for an Arduino Mega:

 C++ |  copy code |? 
  1. /*
  2. This sketch implements a simple dimmer using a rotary encoder. The
  3. encoder is connected to pins 21 and 22.
  4. The standard led on pin 13 will be dimmed using pwm.
  5. More on: https://blog.linformatronics.nl/58/electronics/simple-rotary-encoder-controlled-pwm-dimmer
  6. */
  7. // Arduino Mega1280
  8. /*
  9.   Encoder state
  10.    B      A
  11.   red  yellow Numeric
  12.    1      1      3
  13.    1      0      2
  14.    0      0      0
  15.    0      1      1
  16. */
  17. const uint8_t pwmPin = 13;
  18. const uint8_t encoderPinA = 21;      // encoder input channel A
  19. const uint8_t encoderPinB = 22;      // encoder input channel B
  20. const uint8_t interruptChannel = 2;  // pin 21, encoder input channel A
  21. const uint8_t stepSize = 1;          // step size to increase PWM setting
  22. volatile uint8_t analogValue = 128;  // initialize at 50% PWM
  23. static void interruptHandler(){
  24.   delay( 10 );                       // debounce
  25.   if ( digitalRead( encoderPinA ) != digitalRead( encoderPinB ) ) {
  26.     if ( analogValue <= ( 255 - stepSize ) ) {
  27.       analogValue += stepSize;
  28.     }
  29.   } else {
  30.     if ( analogValue >= stepSize ) {
  31.       analogValue -= stepSize;
  32.     }
  33.   }
  34. }
  35. void setup(){
  36.   analogWrite( pwmPin , analogValue );
  37.   pinMode( pwmPin , OUTPUT );
  38.   pinMode( encoderPinA , INPUT_PULLUP );
  39.   pinMode( encoderPinB , INPUT_PULLUP );
  40.   
  41.   attachInterrupt( interruptChannel , interruptHandler , CHANGE );
  42. }
  43. void loop() {
  44.   analogWrite( pwmPin , analogValue );
  45.   delay( 100 );
  46. }

Notice that the interrupt pins vary with Arduino board type, check its product page which exact pins you have to use and don’t forget to update the settings for interruptChannel and encoderPinA.

Troubleshooting cronjobs using an interactive shell

November 25th, 2012

One of the most common little annoyances on Linux machines is troubleshooting cronjobs. You write a little script that you want to be run from cron and you test it extensively on you command line. Once the script runs flawlessly you schedule it to run somewhere during the night, only to find out the next morning that nothing happened.

Puzzled by why nothing happened you start running the script from the command line where, of course, all works fine. Searching for the problem you realize the environment when run from cron must be different from your normal shell. You start running the job from cron, adding debugging messages, redirecting the errors to a temporary file, and reconfiguring the crontab to run the job in one or two minutes … and again in one or two minutes … and again in one or two minutes … . Step by step every little (usually PATH-related) problem is solved by manually rescheduling the job. Struggling to find more details, waiting for cron to re-run the job, it can take a long time to solve all issues with the script.

If only you were able to run an interactive shell with an environment set identical as if it was run from cron! The script below does exactly that. It sets an excellent environment to debug scripts that are required to run from cron. It allows you to execute individual commands and inspect its result one by one, just like you would troubleshoot a regular shell script. No more waiting for cron to fire the job only find out you still overlooked another little issue.

The ‘cronShell.sh’ script below is an optimized version that can be directly used on most Linux machines. It automatically adapts for the current user.

 Bash |  copy code |? 
  1. #!/bin/bash
  2. # This script starts a shell with an environment that has
  3. # identical environment as a shell script started from cron.
  4. # More info: https://blog.linformatronics.nl/16/linux/troubleshooting&minus;cronjobs
  5. user=$( id -nu )
  6. cd && env -i sh -c "
  7. ### Ubuntu Linux 12.10
  8. export HOME='${HOME}'
  9. export LANG='en_US.UTF&minus;8'
  10. export LANGUAGE='en_US:en'
  11. export LOGNAME='${user}'
  12. export PATH='/usr/bin:/bin'
  13. export PWD='${HOME}'
  14. export SHELL='/bin/sh'
  15. ###
  16. exec sh"

And this is what it looks like when you run the script:

 Text |  copy code |? 
jhendrix@diablo:~$ cronShell.sh $ set HOME='/home/jhendrix' IFS=' ' LANG='en_US.UTF&minus;8' LANGUAGE='en_US:en' LOGNAME='jhendrix' OPTIND='1' PATH='/usr/bin:/bin' PPID='32184' PS1='$ ' PS2='> ' PS4='+ ' PWD='/home/jhendrix' SHELL='/bin/sh' $ # Type your commands here ...

Credit where credit is due: This post is heavily inspired on an excellent answer by Stephane Chazelas on Unix.StackExchange.com.