Category Archives: Uncategorized

ESPHome TopGreener setup

The TGWF115PRM can be used with ESPHome – the process I used may be somewhat convoluted, but I can confirm it works well and accurately shows power consumption (Watts) through measurement of voltage and current.

Example display in HomeAssistant dashboard

[A note for future readers]

This was written in December 2021. I probably won’t update this in the future – so if you’re reading this sometime far in the future, you may want to look for a newer guide!

Step One

Plug in the device and use Tuya-Convert to load tasmota-lite.bin (see Tuya docs at that link)

Step Two

Make an ESPHome binary (or use mine)

I have the ESPHome integration installed on my Home Assistant server. Select the ESPHome button in the left-side panel in Home Assistant:

Then click on the “Add new device” button, the green circle:

You should name it something useful but short, and you can put in your WiFi info.

Select

The TopGreener smart plug is an ESP8266 device.

Click “EDIT” to change the configuration for your new ESPHome device.

Copy and paste the following configuration into the text window. Makes sure you don’t have duplicate data which was automatically generated. For example you can’t have two esphome, logger, api, ota, or wifi sections. Should be pretty obvious.

esphome:
  name: splug
  platform: ESP8266
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "ota_pass"

wifi:
  ssid: "netgear"
  password: "notmypassword"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Splug Fallback"
    password: "somepw"

switch:
  - platform: gpio
    name: "Smartplug Relay"
    pin: GPIO14
    id: relay
  
binary_sensor:
  - platform: gpio
    name: "Smartplug Button"
    pin: GPIO03
    filters:
    - invert:
    on_click:
          min_length: 50ms
          max_length: 350ms
          then:
            - switch.toggle: relay
  
status_led:
    pin: 
      number: GPIO01
      inverted: true
#      number: GPIO13
#      inverted: true

sensor:
  - platform: hlw8012
    sel_pin: GPIO12
    cf_pin: GPIO04
    cf1_pin: GPIO05
    current:
      name: "Smartplug Current"
    voltage:
      name: "Smartplug Voltage"
    power:
      name: "Smartplug Power"
    energy:
      name: "Smartplug Energy"
    update_interval: 15s
    voltage_divider: 14850
    current_resistor: .0127

captive_portal:

Change the details highlighted with red text to the appropriate values.

Step Three

Select “INSTALL”

Then select “Manual Download”
Download the .bin file and save it somewhere safe.

Connect your Tasmota device to your WiFi

But first — You have to now connect to the Tasmota backup hotspot that shows up when you plug in the smart plug and tell it to connect to your wifi router. Open up your phone or laptop and connect to the WiFi AP which shows up as something like: “tasmota-xxxxx

Open up your web browser and you should find a page that has fields such as:

Input your home WiFi AP password and SSID under “AP1”. You can leave the other fields as they are. Click “Save”

Then I had to login to my LAN router to see what IP address was assigned to the new tasmota-0688 device. Unfortunately I wasn’t able to navigate to the device in Firefox. I suspect Tasmota isn’t using mDNS (though my build was a year old — apparently Tasmota has mDNS support now)

So if you have a newer build of Tasmota on your device you can try to navigate to the Tasmota device setup page by going to the url: http://tasmota-nnnn.local/ of course the “nnnn” refers to the ID which is automatically generated. My device in this example gave itself an ID of 0688, so I would attempt to go to http://tasmota-0688.local/ to get to the setup page.

This is from my router’s DHCP info. I had to go here because Tasmota didn’t support mDNS.

BUT anyway I had to login to my router to get the IP address, so I went to (for example): http://192.168.60/

If your browser forces you to go to https then you’ll have to figure out how to turn that off because it will fail.

Upload the bin file to the smart plug

Last step! When you get to the Tasmota setup page, select Firmware Upgrade

Find the .bin file you created earlier from HomeAssistant and upload it to your smart plug device.

You can close that tab, you won’t see that page again.

If this was successful, your ESPHome device should be detected in Home Assistant:

Click “Check it out”:

Then “CONFIGURE”

Select an area to add it to, then FINISH

Back in the Home Assistant Overview page, we see our new device:

A note on Calibration

The ESPHome configuration has two constants for voltage/current calibration. I chose these numbers based on my own measurements and devices. They may be accurate for you, but if not, do not despair. If you attach a known load, such as a 100W incandescent bulb, you can use change these values to fairly accurately reflect the load. If you have a decent way to measure the AC current and AC line voltage, you can do that too.

voltage_divider: 14850
current_resistor: .0127

The “Smartplug2 Voltage” value shows an odd value, 10.5 V, because the Relay is turned off. When we turn it on, with a load, it shows up with the correct value of 117 V

The first step to calibrate the device would be to measure the actual AC line voltage. If I measured 118.2 V I could change the voltage_divider value by multiplying it by the ratio of new/old:

voltage_divider = 14850 * 118.2 / 117.0 = 15002

The same procedure applies to the current_resistor value, except that increasing the current_resistor value will LOWER the measured current, and vice versa. Therefore you have to invert the ratio. If the measured current was 0.36 A then you would set:

current_resistor = 0.0127 * 0.35 / 0.36

Finally

I hope this was useful in all or part. I realize going from tuya-convert to Tasmota to ESPHome is not direct, but it happens to be the way I did it for this.

References

Tasmota TGWF115PRM configuration

Tasmota Documentation

Tuya-convert

Altium libraries – capacitors and resistors

I generated several libraries for Altium (or Circuit Studio) using Database Libraries which I then exported to IntLibs.

I realize you can get many of these parts from Altium or other third parties. I wanted to figure out on my own how I could scrape data from Digi-Key and (almost) automatically create part libraries.

Altium Designer has a powerful feature called Database Libraries which take an external database and link entries to a schematic symbol and physical footprint. The database can take many forms such as an Excel spreadsheet, Microsoft Access database, or in theory any SQL or ODBC source.

I’ll start here by giving you the libraries I generated and I’ll make another post about how I generated them.

INTLIBs (for Altium or Circuit Studio):

Panasonic ERJ resistors libraries download

Murata capacitors libraries download

DBLIBs (for Altium only):

Panasonic ERJ resistors Altium DBLIB

Murata capacitors Altium DBLIB

 

There are 6,029 resistors, all are Panasonic ERJ series. The resistors are split up into 6 different IntLibs according to package size: 0201, 0402, 0603, 0805, 1206, 2512. I scraped the data from Digi-Key to create these libraries and thus they contain a significant number of parameters that you can use for your BOM generation.

The capacitor libraries are similar. There are 4,739 capacitors in 6 different libraries split up according to package size: 0201, 0402, 0603, 0805, 1206, 1210. They all contain similar parameters as the resistors. All capacitors are Murata – because it was easier to scrape one manufacturer without getting thousands of duplicate value parts.

 

PT100 interface to I2C (Raspberry Pi)

I was looking for a way of reading a PT100 temperature sensor with a Raspberry Pi. I couldn’t find an inexpensive way of accomplishing this on a hobbyist budget. And to be honest I like to build things so this was how I did it.

pt100

Amazon and eBay are full of things like this now. This is a $10 PT100 temperature sensor. I wouldn’t use this for a commercial application but it works just fine and I personally trust it for my beer brewing application.

It’s a three terminal sensor. A PT100 thermistor is just a resistor that varies with temperature. The resistance goes up when the temperature goes up. For a chart, see the wikipedia page.

There are three ways to hook up a thermistor.

Typically you have somewhat long, thin, wires so the resistance is not negligible.

The resistance of a PT100 varies at about 0.38 ohms per degree celsius, so if your wires measure a total of 0.5 ohms, you’re off by over 1 degree. And the thermal coefficient of copper is significant (3.8 Ohm/Ohm/K) so your wire resistance cannot be linearly calibrated out.

The first way is to use two wires.

pt100_2wire

R1 and R2 represent the two wires leading to the thermistor.

The thermistor (R3) is sensed by measuring the voltage V = V1 – V2

And you calculate R3 by using Ohm’s law, R = V / I

R2 and R1 can be measured but like I talked about, you can’t just calibrate these.

Nobody does this except when the leads are very short. For example if your thermistor is on the PCB with the ADC.

On to method #2:

Three wire thermistor sensing

pt100_3wire

The 1mA current flows from the positive supply through the wire (R2) through the thermistor (R3) and through the final wire (R1) before reaching the current sink (I1) (and on to ground)

Here I added another wire represented by R4 and terminated at the node V3.

Almost no current will be flowing in or out of V3 – we’re just using it to measure the voltage on the other side of R1.

We can use this extra wire (V3) to measure the voltage drop across resistor R1 because we also know the voltage V2. The voltage drop across R1 is:

V(R1) = (V3-V2)

which also happens to be the same as the voltage drop across R2. The wires are the same size and length (approximately)

V(R2) ≈ (V3-V2)

Now that we have a third wire (V3) we should use this in place of V2 to measure the thermistor. Consider the value (V1-V3). This eliminates the voltage drop due to R1 but it still has the voltage drop due to the 1mA current flowing through R2. Since we already calculated the voltage drop V(R2) we can simply subtract it, giving the voltage across the thermistor Vtherm:

Vtherm = (V1-V3) - V(R2)
or
Vtherm = (V1-V3)-(V3-V2)

Four wire sensing

Four wire sensing is the easiest but nobody uses it. You need an extra wire.

pt100_4wire

I added a wire represented by R5 and terminated into node V4 in the above schematic. Measuring the thermistor would be really simple if you have a four wire setup: you just measure V4 and V3 with a voltmeter (or differential ADC). The voltage across the thermistor is (V4-V3).

On to the PCB!

The following board that I’m sharing is free-for-all. It’s not perfect but it worked for me the first time around and I was incredibly surprised. As an engineer you come to expect your designs to be nearly FUBAR on the first try. Actually – I’m being modest; they often work reasonably well. I just stopped being modest. I’m sorry.

Here’s the schematic. Click the image to see a larger view:

pt100_amp

This board has two current sinks. Each sinks 1.0mA with a reasonably wide compliance range (TBD).

The current sinks are connected between the EX+ and EX- pins. The ADC inputs are connected across SENSE+ and SENSE-.

The current sink is fixed at 1mA.

The ADC is the Microchip MCP3427. It is a 16 bit I2C converter with an onboard 2.048v reference. It also has a programmable gain amplifier (PGA) that can multiply the analog input by 2, 4, or 8. This allows you to achieve higher dynamic range in the digital domain. The analog inputs are differential so you can measure a differential voltage without adding external op-amps, which is perfect for this application.

Let’s say you want to measure between -50 Celsius and +200 Celsius using a PT100 with a 1 mA current sink. The Wikipedia chart shows that the resistance varies between about 80 and 176 ohms and thus the voltage is 80 mV to 176 mV. Enabling the x8 PGA will multiply these values by 8, giving 640 mV minimum to 1.408 volts maximum.

The voltage is then converted by the 16 bit ADC with 2.048v reference. This gives

0.64 / 2.048 * 2^16 = 20480 LSB for -50 Celsius

and

1.408 / 2.048 * 2^16 = 45056 LSB for +200 Celsius

This is 24576 LSB over 250 degrees, or about 98 LSB per degree. Or (about) 0.01 degrees per bit. This is completely neglecting linearization issues. I’m just showing you the dynamic range of the signal path.

This design is not ideal for three wire PT100 sensors, but it does work just fine if you don’t have long lead wires. You could use the second ADC channel as the wire compensation channel. Just subtract the wire voltage drop from the primary channel reading.

If you don’t care about the static offset due to one of the PT100 sensor wires, you can use this board as a two-channel device. Just hook up the single red wire to both the EX+ and SENSE+ lines and hook up one blue wire to EX- and the other blue wire to SENSE-.

(The colors could be anything, but there will be one of color ‘A’ and two of color ‘B’)

I made the decision to leave the terminals independent so that you can easily attach a different type of sensor to the ADC.

As usual, I had OSH Park make the boards and I’m sharing the project so that anyone can order boards for themselves. Here’s a link to the OSH Park page for this project.

pt100_bot pt100_top

Bill of materials:

pt100_bom

Finished board! Sorry it’s a bit messy.

 

 

MMBT5179 amplifier board

The MMBT5179 is a cheap NPN bipolar junction transistor designed for radio frequency applications. I designed this super simple 1″ square board to test the performance of this transistor.

I personally intend to use this part in the HF range from 10 – 30 MHz. Due to this being a two layer board I was unable to make the trace impedance 50 ohms. The traces would need to be >100 mils wide. However, the traces are extremely short with this being such a small PCB.

As configured, the bias current should be about 11 mA. Intended supply voltage is 5.

Simulation shows gain of +20dB at 20 MHz. We’ll see!

This PCB is shared on OSH Park. It’ll cost about $5 for three copies.

HF/VHF photodiode amplifier

photodiodeamp

Recently at work I was tasked with determining the frequency modulation of a laser beam. I designed and tested the above circuit in a couple hours.

I tested the circuit first with no photodiode in place using my DG8SAQ vector network analyzer — a very low cost network analyzer based on the highly respected (by me) AD9852 DDS chip.

The circuit showed >+20dB gain from about 4 MHz to the cutoff frequency of the RLP-137. Hooray for small victories!

For the photodiode, I had no choice because I just had one laying around here that I could find the part number for: a Melles Griot 13DSI001. This particular part has a responsivity of 0.45 Amps/Watt at 830nm, breakdown voltage of 60, and 10pF capacitance.

A photodiode is typically reverse biased as you see above, with a positive voltage on the cathode. In my case I don’t care very much about what voltage I’m using as long as I do not exceed the breakdown voltage (more about that coming up.)

As light becomes incident on the diode junction, carriers are created that allow current to flow from the high potential to the low potential. This holds true for high frequencies; a high frequency alternating current is allowed through the device.

When a current source is terminated into a resistor we get a voltage source having an impedance of the resistor value. The photodiode is terminated into a 50 ohm resistor, hence we have a fairly decent 50 ohm source that is converting light modulation into RF of a convenient impedance. The maximum frequency (as I understand) is limited by the 10pF intrinsic capacitance of D1, combined with the load resistance, R3. This gives a cutoff frequency of 1/(2*Pi*RC) of about 300 MHz. I may not have needed the lowpass filter (RLP-137), however with my shoddy perf-board construction I was worried about the GALI-74 potentially oscillating in the GHz range.

The 1k resistor R1 is to limit the maximum current that can pass through the photodiode. Combined with C3, this provides filtering from feedback or noise that could come through VCC.

I’d be happy to see your comments below if you have corrections or advice.

MMIC photodiode amplifier
MMIC photodiode amplifier

Writing a PCI device driver

I’m writing a Linux device driver for the PCI/PCIe cards 5i25 and 6i25 available from MESA electronics[1]

I’ve never written a device driver before. Currently my driver does nothing except enable the PCI device, map the BAR0 to system address space, and check the “magic value” available in BAR0 + 0x100.

It then prints out all of the probed module descriptors that correspond to available hardware functionality.

The magic value is an arbitrary number to test that your driver is accessing the PCI device correctly.

The next step is to accept module load-time parameters and configure the board as desired. Mesa has made a very capable board with the HOSTMOT2 firmware[4]. It has many options and many configuration registers.

Beyond configuration, the driver will have to accept IOCTL calls from user programs and drive the 6i25.

Here’s the code:

/*  
 *  ni25.c
 * 
 * Author: Martin Klingensmith
 * Description: Driver for Mesa electronics 5i25 and 6i25 PCI[e] boards
 * 
 */
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/module.h>	/* Needed by all modules */
#include <linux/kernel.h>	/* Needed for KERN_INFO */

#define DRIVER_AUTHOR "Martin Klingensmith <martin@nnytech.net>"
#define DRIVER_DESC "A driver for MESA electronics 5i25/6i25 IO cards"
#define DRIVER_NAME "ni25"

#define MAGIC_NUMBER 0x55AACAFE	/* Magic number defined by Mesa */
#define LEDS_OFFSET 0x0200
#define IDROM_OFFSET 0x010C	/* Location of IDROM offset value */

struct ni25_idrom {
  u32			IDROMType;
  u32			OffsetToModules;
  u32			OffsetToPindesc;
  u32			BoardNameLow;
  u32			BoardNameHigh;
  u32			FPGASize;
  u32			FPGAPins;
  u32			IOPorts;
  u32			IOWidth;
  u32			PortWidth;
  u32			ClockLow;
  u32			ClockHigh;
  u32			InstanceStride0;
  u32			InstanceStride1;
  u32			RegisterStride0;
  u32			RegisterStride1;
};

struct module_descriptor {
  u8			GTag;		/*General function tag					*/
  u8			Version;	/*module version					*/
  u8			ClockTag;	/*Whether module uses ClockHigh or ClockLow		*/
  u8			Instances;	/*Number of instances of module in configuration	*/
  u16			BaseAddress;	/*offset to module. This is also specific register = Tag*/
  u8			Registers;	/*Number of registers per module			*/
  u8			Strides;	/*Specifies which strides to use			*/
  u32			MPBitmap;	/*bit map of which registers are multiple
					  '1' = multiple, LSb = reg(0)				*/
};

struct pin_descriptor {
  u8			SecPin;		/*Which pin of secondary function connects here 
					  eg: A,B,IDX. 
					  Output pins have bit 7 = '1'*/
  u8			SecTag;		/*Secondary function type (PWM,QCTR etc). 
					  Same as module GTag*/
  u8			SecUnit;	/*Which secondary unit or channel connects here*/
  u8			PrimaryTag;	/*Primary function tag (normally I/O port)*/
};

struct ni25_brd {
  unsigned long           paddr;
  void __iomem            *regs;	/* Board BAR0 mapped location */
  u32                     iolen;
  int                     irq;
  u16                     bus_num;
  struct device           *parent_dev;
  u16                     num_cs;         /* supported slave numbers */

};

struct ni25_dev {
  struct pci_dev *pdev;
  struct ni25_brd brd;
  struct ni25_idrom idrom;
  u32	idrom_offset;
  struct module_descriptor module[32];
  u32	num_modules;
};

/* 
 *
 * ni25_pci_probe 
 *
 */
static int ni25_pci_probe(struct pci_dev *pdev,
	const struct pci_device_id *ent)
{
	struct ni25_dev *ni25;
	struct ni25_brd *brd;
	int pci_bar = 0;
	int ret;
	int v=0;
	void __iomem *regs;
	char name[9];

	printk(KERN_INFO "NI25: found PCI card (ID: %04x:%04x)\n",
		pdev->vendor, pdev->device);

	ret = pci_enable_device(pdev);
	if (ret)
		return ret;

	ni25 = kzalloc(sizeof(struct ni25_dev), GFP_KERNEL);
	if (!ni25) {
		ret = -ENOMEM;
		goto err_disable;
	}

	ni25->pdev = pdev;
	brd = &ni25->brd;

	/* Get basic io resource and map it */
	brd->paddr = pci_resource_start(pdev,pci_bar);
	brd->iolen = pci_resource_len(pdev, pci_bar);

	printk(KERN_INFO "NI25: address %04x length %04x\n",(unsigned int)brd->paddr,(unsigned int)brd->iolen);

	ret = pci_request_region(pdev, pci_bar, dev_name(&pdev->dev));
	if (ret)
		goto err_kfree;

	regs = ioremap_nocache((unsigned long)brd->paddr,
				pci_resource_len(pdev, pci_bar));
	brd->regs = regs;

	if (!brd->regs) {
	       printk(KERN_INFO "NI25: ioremap_nocache call failed.\n");
		ret = -ENOMEM;
		goto err_release_reg;
	}

	/* Read magic number from card register */
	v = readl(regs + 0x100);
	if(v != MAGIC_NUMBER){
	  printk(KERN_INFO "NI25: unknown magic number. %04x != %04x\n",MAGIC_NUMBER,v);
	  ret = -1;
	  goto err_unmap;
	}

	/* Get configuration name */
	for(v=0;v<8;v++){
	  name[v] = ioread8(regs+0x104+v);
	}
	name[8]=0;
	printk(KERN_INFO "NI25: config name: [%s]\n",name);

	ni25->idrom_offset = ioread32(regs+IDROM_OFFSET);

	printk(KERN_INFO "NI25: idrom offset: %04x\n",ni25->idrom_offset);

	/* Read IDROM */
	memcpy(&ni25->idrom,regs+ni25->idrom_offset,sizeof(struct ni25_idrom));

	printk(KERN_INFO "NI25: modules offset: %04x\n",ni25->idrom.OffsetToModules);

	/* Read all modules descriptors */
	memcpy(ni25->module,regs+ni25->idrom.OffsetToModules + ni25->idrom_offset,0xC * 32);

	for(v=0;v<32;v++){
	    if(ni25->module[v].GTag==0){
	      ni25->num_modules = v-1;
	      break;
	    }
	    printk(KERN_INFO "NI25: module %d \n\tGTag=[%04x]\n\tVersion=[%04x]\n\tClockTag=[%04x]\n\tInstances=[%04x]\n\tBaseAddress=[%04x]\n\tRegisters=[%04x]\n\tStrides=[%04x]\n\tMPBitmap=[%04x]\n"\
							    ,v,ni25->module[v].GTag, \
							    ni25->module[v].Version, \
							    ni25->module[v].ClockTag, \
							    ni25->module[v].Instances, \
							    ni25->module[v].BaseAddress, \
							    ni25->module[v].Registers,\
							    ni25->module[v].Strides,\
							    ni25->module[v].MPBitmap);
	}

	printk(KERN_INFO "NI25: found %d modules.\n",ni25->num_modules);

	brd->parent_dev = &pdev->dev;
	brd->bus_num = 0;
	brd->num_cs = 4;
	brd->irq = pdev->irq;

	pci_set_drvdata(pdev, ni25);
	return 0;

err_unmap:
	iounmap(brd->regs);
err_release_reg:
	pci_release_region(pdev, pci_bar);
err_kfree:
	kfree(ni25);
err_disable:
	pci_disable_device(pdev);
	return ret;
}

/* 
 *
 * 
 * ni25_pci_remove 
 * 
 * 
 */
static void ni25_pci_remove(struct pci_dev *pdev)
{
	struct ni25_dev *ni25 = pci_get_drvdata(pdev);
	printk(KERN_INFO "ni25: unloading...\n");
	iounmap(ni25->brd.regs);
	pci_release_region(pdev, 0);
	kfree(ni25);
	pci_disable_device(pdev);
}
/* 
 *
 * ni25_suspend */
#ifdef CONFIG_PM
static int ni25_suspend(struct pci_dev *pdev, pm_message_t state)
{
	pci_save_state(pdev);
	pci_disable_device(pdev);
	pci_set_power_state(pdev, pci_choose_state(pdev, state));
	return 0;
}
/* ni25_resume */
static int ni25_resume(struct pci_dev *pdev)
{
	u32 ret=-1;
	pci_set_power_state(pdev, PCI_D0);
	pci_restore_state(pdev);
	ret = pci_enable_device(pdev);
	if (ret)
		return ret;
	return 0;
}
#else
#define spi_suspend	NULL
#define spi_resume	NULL
#endif

/* Documentation */
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);

static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
	/* Intel MID platform SPI controller 0 */
	{ PCI_DEVICE(0x2718, 0x5125) },
	{},
};

static struct pci_driver ni25_driver = {
	.name =		DRIVER_NAME,
	.id_table =	pci_ids,
	.probe =	ni25_pci_probe,
	.remove =	ni25_pci_remove,
	.suspend =	ni25_suspend,
	.resume	=	ni25_resume,
};

module_pci_driver(ni25_driver);

Here are the dmesg lines when the module is loaded:

[64520.720535] NI25: found PCI card (ID: 2718:5125)
[64520.720553] NI25: address fe700000 length 10000
[64520.720606] NI25: config name: [HOSTMOT2]
[64520.720610] NI25: idrom offset: 0400
[64520.720635] NI25: modules offset: 0040
[64520.720771] NI25: module 0 
[64520.720771]  GTag=[0002]
[64520.720771]  Version=[0000]
[64520.720771]  ClockTag=[0001]
[64520.720771]  Instances=[0001]
[64520.720771]  BaseAddress=[0c00]
[64520.720771]  Registers=[0003]
[64520.720771]  Strides=[0000]
[64520.720771]  MPBitmap=[0000]
[64520.720783] NI25: module 1 
[64520.720783]  GTag=[0003]
[64520.720783]  Version=[0000]
[64520.720783]  ClockTag=[0001]
[64520.720783]  Instances=[0002]
[64520.720783]  BaseAddress=[1000]
[64520.720783]  Registers=[0005]
[64520.720783]  Strides=[0000]
[64520.720783]  MPBitmap=[001f]
[64520.720794] NI25: module 2 
[64520.720794]  GTag=[0004]
[64520.720794]  Version=[0002]
[64520.720794]  ClockTag=[0001]
[64520.720794]  Instances=[0002]
[64520.720794]  BaseAddress=[3000]
[64520.720794]  Registers=[0005]
[64520.720794]  Strides=[0000]
[64520.720794]  MPBitmap=[0003]
[64520.720805] NI25: module 3 
[64520.720805]  GTag=[00c1]
[64520.720805]  Version=[0000]
[64520.720805]  ClockTag=[0001]
[64520.720805]  Instances=[0001]
[64520.720805]  BaseAddress=[5a00]
[64520.720805]  Registers=[0006]
[64520.720805]  Strides=[0010]
[64520.720805]  MPBitmap=[003c]
[64520.720816] NI25: module 4 
[64520.720816]  GTag=[0006]
[64520.720816]  Version=[0000]
[64520.720816]  ClockTag=[0002]
[64520.720816]  Instances=[0002]
[64520.720816]  BaseAddress=[4000]
[64520.720816]  Registers=[0005]
[64520.720816]  Strides=[0000]
[64520.720816]  MPBitmap=[0003]
[64520.720828] NI25: module 5 
[64520.720828]  GTag=[0005]
[64520.720828]  Version=[0002]
[64520.720828]  ClockTag=[0001]
[64520.720828]  Instances=[0008]
[64520.720828]  BaseAddress=[2000]
[64520.720828]  Registers=[000a]
[64520.720828]  Strides=[0000]
[64520.720828]  MPBitmap=[01ff]
[64520.720839] NI25: module 6 
[64520.720839]  GTag=[0080]
[64520.720839]  Version=[0000]
[64520.720839]  ClockTag=[0001]
[64520.720839]  Instances=[0001]
[64520.720839]  BaseAddress=[0200]
[64520.720839]  Registers=[0001]
[64520.720839]  Strides=[0000]
[64520.720839]  MPBitmap=[0000]
[64520.720848] NI25: found 6 modules.

 

References

[1] Mesa Electronics
http://www.mesanet.com/

[2] The Linux Kernel Module Programming Guide
http://www.tldp.org/LDP/lkmpg/2.6/html/x121.html

[3] How To Write Linux PCI Drivers by Martin Mares <mj@suse.cz> on 07-Feb-2000
http://galileo.phys.virginia.edu/~rjh2j/l2beta/software/pci.txt

[4] LinuxCNC Documentation wiki
http://wiki.linuxcnc.org/cgi-bin/wiki.pl?HostMot2

 

Mapping Google Drive or Dropbox to a drive letter

So you’re on Windows and want to use Dropbox or Google Drive with it’s own drive letter?

Step 1:
First figure out where your Dropbox or Google Drive folder is stored. Go to ‘Computer’ and right click on Dropbox or Google Drive and copy the whole path name that is in the text field labeled Target. Mine happens to be:
“E:\googledrive\Google Drive”

But yours is probably something like:
“C:\Users\Martin\Google Drive”

Make sure you copy the quotes too.

Step 2:
Run notepad and put one line in the new file. Replace the path with the one you found in step 1.

Now, save this file on your Desktop as a file named: drive letter.bat

Close notepad.

Step 3:
Double-click the file you just saved. You should have a new drive called G:
If not, you did something wrong.

Step 4:
Drag this batch file (drive letter.bat) to the start menu, and put it in:
Start->All Programs->Startup

It’ll start up every time you login to your computer, and if everything went right you’ll always have a drive G: