Die wilde Maus          


von Ralf Beesner    
Elektronik-Labor   Projekte   AVR 



Mit dem VUSB-Treiberpaket kann man fast jeden AVR-Mikrocontroller in ein USB-Lowspeed-Gerät verwandeln. Die unter http://www.obdev.at/products/vusb/downloads-de.html bereitgestellten Quellcode-Pakete des VUSB-Treibers enthalten als Demo-Anwendung eine virtuelle Maus, die den Mauszeiger auf dem Bildschirm langsam im Kreis bewegt. Diese Beispielanwendung lässt sich mit wenig Aufwand in eine Spaß-Schaltung umwandeln.

Ich habe den Code so modifiziert, dass der Mauszeiger stattdessen nach Ablauf einer Zufallszeit zu zittern beginnt (Versionsname: Tremor) bzw. nach Ablauf der Zufallszeit alle paar Sekunden willkürliche Sprünge in alle vier Richtungen ausführt (Version: Weird_Mouse).


Hardware

Verwendet wird die gleiche Hardware wie beim Windows-Rebooter. Schaltplan und Layout-Vorschlag sind daher ohne nähere Erläuterung beigefügt.



Update 17.9.12: in den AtTiny45- Schaltbildern waren die beiden USB- Datenleitungen vertauscht




Tremor

Die Software verhält sich für eine Zufallszeit von 2-70 Minuten passiv und legt dann den 1,5 kOhm-Widerstand auf high, um sich beim Betriebssystem als Low-Speed-Gerät anzumelden (die Testversion main-test.hex wartet nur 10 Sekunden).

Nach erfolgreicher Anmeldung springt sie aus der Main Loop nach jedem USB-Interrupt in die Funktion drunken_Pointer. Diese erzeugt jedesmal mit Hilfe der rand()-Funktion neue X-und Y-Koordinaten für den Mauszeiger. Die Konstante 2 und der Teilerwert 6200 sind experimentell ermittelt; sie bewirken, dass die Koordinaten um den Wert 0 herumzittern und der Mauszeiger nicht rasch wegdriftet.




Weird_Mouse

Diese Version verhält sich zunächst gleich wie die Tremor-Version; in der Main Loop ruft sie jedoch die Funktion weirdPointer auf.

In dieser Funktion wird die rand()-Funktion jeweils drei Mal verwendet, um Zufallswerte für die Variablen "Delay", "direction" und "distance"  zu erhalten. Die Random-Werte werden per Shift-Befehl durch 512, 8192 und 256 geteilt, damit die Variablen im jeweils passenden Wertebereich liegen. Der Zufallswert für "Delay" sorgt dafür, dass der Mauszeiger in unregelmäßigen Zeitabständen springt, "distance"  bestimmt die Sprungweite und "direction" die Richtung.

Ist Delay noch nicht abgelaufen, sendet die Software als x-und y-Koordinate jeweils eine "0". Ähnlich wie beim USB-Keyboard verhält sich auch der PCMaustreiber nach der Devise: "der letzte Befehl gilt". Sendet man zwischendurch keine Nullen, "flitzt" der Mauszeiger sofort an den Bildschirmrand.


Flashen

Die erforderliche Fusebytes sind genau so wie beim Windows Rebooter:
lfuse=0xef aktiviert den Quarzoszillator
hfude=0xdd aktiviert den Brownout-Detektor mit einer Schwelle von 2,7V


Anwendung

Die Schaltung verhält sich zufallsgesteuert für 2 -70 min. passiv (damit man nach Platzierung des Schaltung erst mal "das Weite suchen kann"). War noch keine HID-Maus in dem USB-Port des Rechners eingesteckt, ploppt danach der Treiberdialog auf. Windows hat zwar den passenden Treiber (HID-Treiber sind Teil der Windows-Installation), er ist aber noch nicht an den benutzten USBPort gebunden. Das Opfer klickt dann hoffentlich auf "automatisch installieren" -die meisten Windows-Opfer sind ja konditioniert, kryptische Windows-Dialoge mit "ja" oder "weiter" wegzuklicken.

Danach beginnen entweder das Mauszittern oder die unregelmäßigen Sprünge des Mauszeigers. Nach einem Reboot oder Kaltstart läuft zunächst wieder die trügerische Wartezeit ab, bis die Schaltung erneut zuschlägt.

Download:
Tremor-Software.zip
Weird_Mouse-Software.zip

/* Name: main.c
* Project: hid-mouse, a very simple HID example
* Author: Christian Starkjohann
* Creation Date: 2008-04-07
* Tabsize: 4
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
* This Revision: $Id$
*/

/*
This example should run on most AVRs with only little changes. No special
hardware resources except INT0 are used. You may have to change usbconfig.h for
different I/O pins for USB. Please note that USB D+ must be the INT0 pin, or
at least be connected to INT0 as well.
*/

// Modification:

/*
The original software moves the mouse cursor in a circle around the screen
sinus / cosinus function replaced by a jumping cursor; it does random jumps after a few minutes
*/


#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/interrupt.h> /* for sei() */
#include <util/delay.h> /* for _delay_ms() */
#include <stdlib.h>
#include <avr/pgmspace.h> /* required by usbdrv.h */
#include "usbdrv.h"

/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */

PROGMEM char usbHidReportDescriptor[52] = { /* USB report descriptor, size must match usbconfig.h */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xA1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM
0x29, 0x03, // USAGE_MAXIMUM
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Const,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7F, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xC0, // END_COLLECTION
0xC0, // END COLLECTION
};
/* This is the same report descriptor as seen in a Logitech mouse. The data
* described by this descriptor consists of 4 bytes:
* . . . . . B2 B1 B0 .... one byte with mouse button states
* X7 X6 X5 X4 X3 X2 X1 X0 .... 8 bit signed relative coordinate x
* Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 .... 8 bit signed relative coordinate y
* W7 W6 W5 W4 W3 W2 W1 W0 .... 8 bit signed relative coordinate wheel
*/
typedef struct{
uchar buttonMask;
char dx;
char dy;
char dWheel;
}report_t;

static report_t reportBuffer;
static uchar idleRate; /* repeat rate for keyboards, never used for mice */
static int j;
static int Delay=200 ;
static int direction;
static int distance;

static void weirdPointer(void)
{

j=j+1;
if (j > Delay){
j=0;
// Delay = rand() >> 7; //test mode
Delay = rand() >> 9; //real mode
direction = rand() >> 13; // shifting 13 bit right is a division by 8192
distance = ( rand() >> 8 ) + 32 ; //shifting 8 bit right is a division by 256

if (direction == 3){
reportBuffer.dx = distance;
reportBuffer.dy = 0;
}
else if (direction == 2){
reportBuffer.dx = 0;
reportBuffer.dy = distance;
}
else if (direction == 1){
reportBuffer.dx = -distance;
reportBuffer.dy = 0;
}
else{
reportBuffer.dx = 0;
reportBuffer.dy = -distance;
}
}
else {
reportBuffer.dx = 0;
reportBuffer.dy = 0;
}
}

/* ------------------------------------------------------------------------- */

usbMsgLen_t usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;

/* The following requests are never used. But since they are required by
* the specification, we implement them in this example.
*/
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */
if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */
/* we only have one report type, so don't look at wValue */
usbMsgPtr = (void *)&reportBuffer;
return sizeof(reportBuffer);
}else if(rq->bRequest == USBRQ_HID_GET_IDLE){
usbMsgPtr = &idleRate;
return 1;
}else if(rq->bRequest == USBRQ_HID_SET_IDLE){
idleRate = rq->wValue.bytes[1];
}
}else{
/* no vendor specific requests implemented */
}
return 0; /* default for not implemented requests: return no data back to host */
}

/* ------------------------------------------------------------------------- */

int __attribute__((noreturn)) main(void)

{
uchar i;
PORTB = 0b00011010; /* output PORTB.1 = 1, pullups for unused inputs, */
wdt_disable();
/* Even if you don't use the watchdog, turn it off here. On newer devices,
* the status of the watchdog (on/off, period) is PRESERVED OVER RESET!
*/
/* RESET status: all port bits are inputs without pull-up.
* That's the way we need D+ and D-. Therefore we don't need any
* additional hardware initialization.
*/
// Delay = 30 ; /* test mode: 8 sec waiting, AtTiny keeps passive */
Delay = 480 + ( rand() >>1 ) ; /* 2....70 min waiting, AtTiny keeps passive */
for(j = 0;j < Delay;j++){
_delay_ms(250);
}
DDRB= (1<<1); /* activate 1,5 kOhm to start enumerating */
wdt_enable(WDTO_1S);
usbInit();
usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */
i = 0;
while(--i){ /* fake USB disconnect for > 250 ms */
wdt_reset();
_delay_ms(1);
}
usbDeviceConnect();
sei();
for(;;){ /* main event loop */
wdt_reset();
usbPoll();
if(usbInterruptIsReady()){
/* called after every poll of the interrupt endpoint */
weirdPointer();
usbSetInterrupt((void *)&reportBuffer, sizeof(reportBuffer));
}
}
}

/* ------------------------------------------------------------------------- */


Elektronik-Labor   Projekte   AVR