TAMRON 70 – 300 LENS interface project

TAMRON 70 – 300 Lens test driver, by G8GKU.

This article presents information relating to the interfacing of a TAMRON lens to an Arduino Nano.

Having acquired a well-used TAMRON 70-300 mm A.F. zoom lens, ( Cannon mount ) from a photographer friend, along with the information that the lens now only occasionally operates properly after many years of faultless service, it was decided to attempt to communicate with the lens, as a technical challenge.

Important.  Before proceeding further it has to be made 100% clear that the information presented here relates to a written-off lens with zero financial value. Any use of this project and related information should only be undertaken with the full understanding that interfacing to a lens of financial value may result in financial loss and / or damage to the lens. Be warned.

General Information.  The lens is of the older technology where the auto focus mechanism is driven by a miniature electric motor.

A reader may be tempted to immediately connect the lens to the interface electronics and then to run the code, however,  it is strongly suggested the notes below are read first.

If a fast start is needed then as a very minimum read the paragraphs relating to the wiring to the lens and connect the lens to the electronics only via the recommended resistors as detailed below and ensure the correct voltages are available before running the code.

Information gleaned via the internet indicated the lens communicates with the host camera via a serial protocol, in this case a three wire SPI bus.

Lens electrical connections.  Physical electrical connections between the lens and camera are via contacts arranged in semi-circular layout on the camera-facing mounting ring of the lens.

The lens used has 7 physical connector pads, others may differ.

Details of the individual contacts were quickly found via the internet, a brief
list of the contact pads is below ;

Visually the external lens contacts are distinguished by having a double-width pad placed second from one end of the group, the adjacent single pad is taken as ‘first’ pad in this project.

Taking the first pad, ( as above ) the pads are believed to have functions as listed below ;

Vbat ( +5v ( 6v ? ) power to the lens motor-drive electronics )
Analogue Ground, ( note, this is the double width pad, ground 0v, relating to the motor power )
VDD ( believed to be + 5v )
Data Out ( from camera, into lens )
Data In ( into camera, from lens )
Data Clock ( supplied by camera )
Digital Ground ( ground 0v, for data connection(s) )

N.B. in this interface project the Arduino Nano will provide the signals relating to the ‘ camera ‘  in the list above.

 

Lens ( Motor ) powers supply, ( 5v )

Nominally the lens motor power is supplied to a dedicated lens connector and is separate from the Logic 5v Vcc.  The author supplies the 5v motor supply from a bench power supply set to 5v. It may be possible to use the same 5v as used for the Vcc from the Nano, additional capacitors on the 5v line would be suggested. However, the author has not tested this, only the bench supply being used.

To initially test the overall project, the code will read back the Zoom position without the motor power being supplied, as only the Vcc 5v is needed. This allows a ” gentle start ” to the experiment before the motor 5v is applied for tests requiring it.

 

Interface Wiring.  (  Interface wiring to the lens )
It must be noted that the easiest connection method which is also 100% non invasive to the lens itself, is to use a lens adapter or extender unit, which carries duplicated pins on both sides, fitting between the lens and the camera body.

If available, such an adapter could have the interface wires  fitted in an invasive fashion and as such may be used as a connection platform for the lens interface, thereby not making intrusive change to the lens.

As the above was not an available option and the lens in hand was a zero-cost write-off, the mounting ring and similar parts of the lens were unscrewed and removed, providing access to the inner electronic system parts of the lens. The fixing screws are small scale and are tightly fitted. Use of a clean and un-rounded
screwdriver is recommended, use a firm grip.

After dismantling it will be found that the pads on the outside of the lens are part of a small plastic mounting assembly, held in place by two further small screws, removal of these screws releases the pad assembly. To prevent any chance of the contact pads of the released contact assembly touching other electrical connections, the pad unit was covered with removable insulating tape.

The pad assembly is electrically connected to the internal curved printed circuit board ( PCB ) by means of a short flexible multi-track PCB.

**IMPORTANT**
If the conductors of the above short flexible PCB are inspected it will be seen that the first external pad and the double-size pad are swapped in sequence by the flexible PCB as it terminates to the inner PCB.

Image above shows thin wires soldered to inner curved PCB.

To make the physical wiring interface to the lens, seven thin flexible multi-strand wires were soldered to the inside destination end of the flexible PCB. Each wire being about 50 cm in length. The lens end of each wire being trimmed back to about 1 mm after solder tinning, facilitating small joints. Delicate and careful soldering is required, use a different colour for each wire if possible.

Allowing for the above two-pad reversal, the wires soldered to the inside curved PCB are as below in sequence, starting at the end where the double-width pad terminates to the inner PCB via the flexible PCB ;

Analogue Ground, ( note, this is the ( external ) double width pad, ground 0v, relating to the motor power )
Vbat ( +5v ( 6v ? ) power to the lens motor-drive electronics )
[ items below are in same sequence as per the external pads ] VDD ( believed to be + 5v )
Data Out ( from camera, to lens )
Data In ( into camera, from lens )
Data Clock ( supplied by camera, in our case, by the interface )
Digital Ground ( ground 0v, for data connection(s) )

Due to the operation of the electrical interface within the lens it is important that
resistors are used to allow the lens and the Arduino Nano to interlink and operate without problems which may be caused by code or other errors, also under normal operation.

Do **NOT** directly connect the Nano pins, via wires,  to the LENS electronics.

Please see and use the wiring diagram, as below,  dia 1.

A summary of the wiring diagram is provided below for convenience, resistors in the MOSI and MISO lines are strongly recommended.
The resistors are soldered to the Nano end of the interface wires and heat-shrink insulated.

……  connect Nano MOSI, pin D11, via a 470 Ohm resistor ( 390R to 1k suitable ) to the Dout lens pad.

……  connect Nano MISO, pin D12, via a 470 Ohm resistor ( 390R to 1k suitable ) to the Din lens pad.

Please understand, without resistors in series with pins D11 and D12, it is possible that damage may or will be inflicted upon the lens electronics or the Nano or both if errors occur.

…..  connect Nano D13, logic OUTPUT, Clock signal source, via a 1k resistor to the lens Data Clock input.

…..  connect Nano D8, logic INPUT, pin via a 2k2 resistor to the lens side of the Clock signal from D13

The two resistors for Nano pins D13 and D8 are mandatory.

Explanation.
When in operation, the CPU internal to the lens holds down the
clock signal whilst it is busy, typically post receiving a command.

Connecting the clock drive signal from the Nano to the lens via a resistor, will allow the Nano to drive the clock signal to the lens from Nano pin D13, without either Nano or lens suffering damage when or if the two ends are at opposite logic states, i.e. when the lens holds down the clock signal.

The 2k2 resistor allows the state of the *lens* clock signal( logic 1 or 0 ) to be read back into the Nano via pin D8.

The Code.
There is no intention in the code to provide anything close to a fully specified interface to the lens.
Code provided demonstrates how the interface protocol functions and provides an example which works in conjunction with the lens used by the author.

Functions provided will read the position of the zoom function and will drive the auto focus motor between close in and far out focus, also the Iris can be made to ‘ blink ‘.

Other functions and the related specific command codes are easily found via the internet and interested persons are invited to experiment with the code as they need.

 

code is found here ;

TAMRON

 

 

XML to JSON python script (Also JSON to XML)

Here are 2 python scripts which convert XML to JSON and JSON to XML.

XML to JSON

Create the sample XML file, with the below contents.

sample.xml
<planets>
	<planet>
		<name>
			Earth
		</name>
		<radius>
			6,371km
		</radius>
	</planet>
	<planet>
		<name>
			Jupiter
		</name>
		<radius>
			69,911km
		</radius>
	</planet>
	<planet>
		<name>
			Mars
		</name>
		<radius>
			3,390km
		</radius>
	</planet>
</planets>

Run the below python script and and it will output the converted XML as a file named output.json.

import json
import xmltodict

with open("sample.xml", 'r') as f:
	xmlString = f.read()

print("XML input (sample.xml):")
print(xmlString)
	
jsonString = json.dumps(xmltodict.parse(xmlString), indent=4)

print("\nJSON output(output.json):")
print(jsonString)

with open("output.json", 'w') as f:
	f.write(jsonString)

JSON to XML

Create the sample JSON file, with the below contents.

sample.json
{
    "planets": {
        "planet": [
            {
                "name": "Earth",
                "radius": "6,371km"
            },
            {
                "name": "Jupiter",
                "radius": "69,911km"
            },
            {
                "name": "Mars",
                "radius": "3,390km"
            }
        ]
    }
}

Run the below python script and and it will output the converted JSON as a file named output.xml.

import json
import xmltodict

with open('sample.json', 'r') as f:
	jsonString = f.read()

print('JSON input (sample.json):')
print(jsonString)

xmlString = xmltodict.unparse(json.loads(jsonString), pretty=True)

print('\nXML output(output.xml):')
print(xmlString)

with open('output.xml', 'w') as f:
	f.write(xmlString)

You may need to install the xmltodict module:

pip install xmltodict

Orbitron DDE Azimuth Elevation To Serial

Control a satellite rotator automatically from Orbitron by sending it the azimuth via serial.

A couple of years ago I wrote some code with a library I found to listen to Orbitron using it’s DDE inter-process comms and send the satellite information string over serial to potentially drive an aerial rotator.

I’ve since updated the code and thought I would share it here.

Using DdeOrbitronToSerial

[Drivers]
DDEOrbitronToSerial=C:\blah\DDEOrbitronToSerial.exe
  • Don’t directly run the exe, you have to launch it from Orbtron itself by going to the rotor/radio tab, selecting the correct DDE driver, in this case it’s DDEOrbitronToSerial. Then click the button to the right of the dropdown to start sending data and launch the application:

orbtron-rotor-tab

 

  • The DDEOrbitronToSerial application should now launch and you will be able to select your COM port and output the satellite data to the serial port.
    • NB: You can edit additional options such as com port baud rate in the config file by clicking the open config button.

DDEOrbitronToSerialScreenShot

 

Any bug reports or feature requests are welcome!

Dual AD9850 in Quadrature and finer phase adjustment

Project: Adjustable frequency dual AD9850 DDS units with output adjustable phase from 0 to 180 degrees in 11.25 phase increments.

Platform:
-Arduino Uno R3
-2x 9850 DDS units
-Arduino software written in Arduino IDE version    1.6.8
-Computer running serial terminal client software to send commands over serial port

The AD9850 has five bits that control the phase allowing for 11.25 (360/32) degree phase adjustment resolution.

The original code provides a way to adjust frequency while keeping a constant phase difference of 90 degrees.

The modified code gives finer phase adjustment in 11.25 degree increments from 0 to 180 degrees.

The menu of commands is sent to the serial terminal software when the Arduino is powered on:
COMMAND LIST
as      DDS A sine o/p
ac      DDS A cos o/p
bs      DDS B sine o/p
bc      DDS B cos o/p
To adjust phase type b0000, b1125, b…, b16875, b18000
To set DDSB 90 behind DDSA: set as and bc
To set DDSB 90 ahead of DDSA: set as and b18000
To set DDSB 45 ahead of DDSA: set as and b4500
To setup DDSB 45 behind DDSA: setup as and b13500
To setup DDSB 11.25 behind DDSA: setup as and b7875
To setup DDSB 11.25 ahead of DDSA: setup as and b10125
phase is adjustable by 11.25 degrees from 0 to 180 degrees
Set ac and bc for 180 degrees out of phase. as and bs for in phase
100Hz   example
100kHz  example
1MHz    example
pins    list pins
When unit powers on DDSB lags DDSA by 90 degrees

Laravel TokenMismatchException VerifyCsrfToken.php

While learning Laravel I came across the error:

laravel TokenMismatchException in VerifyCsrfToken.php line 67

This seems to be a breaking change between when the latest laracast tutorials were recorded and the version I am using.

This was due to a hidden field not being on a form which is required when using POST.

The fix was to add:


{!! csrf_field() !!}

For example:


<form method="POST" action="/cards/{{$card->id}}/notes">
<div class="form-group">
<textarea name="body" class="form-control"></textarea>
</div>

<div class="form-group">
<button type="submit" class="btn btn-primary">Add Note</button>
</div>

{!! csrf_field() !!}

</form>


 

Arduino Sleep

I needed to put an Arduino into a low power standby / sleep mode and then have it automatically wake up. I put together this Sleeper class which I have since found useful, thought I’d share it here.

A couple of notes: It uses the watchdog timer (the only timer left running when the CPU is powered down), so if you’re using a watch dog then this may not be what you’re after. Also, it’s not a very accurate timer (a few ms) so don’t use this for highly accurate timing operations – it’s fine for most applications that need to sleep / power down for a period of time and then wake up do some computing and then go back to sleep.

An example program is below, which is made up up of 3 files:

  1. The main .ino Arduino file.
  2. Sleeper.h
  3. Sleeper.cpp

Main File (example usage)


#include "Sleeper.h"

Sleeper g_sleeper;

// Initialisation code
void setup(void) {
    Serial.begin(9600);
    Serial.println("Setup done.");
}

// Main loop
void loop(void) {
    Serial.print("Total milliseconds awake: ");
    Serial.println(millis());
    delay(100);

    // Power down for 10 seconds.
    g_sleeper.SleepMillis(10000);
}


 

Download Example Project

If you’re just using this lib then you can grab the example project here. You don’t need to read the rest of this code unless you’re particularly interested, for those that are here are the .h and .cpp files:

The class I’ve put together uses the watchdog timer, so if you’re using that for you project then you’ll have to do it another way. The reason for using the watchdog timer instead of one of the normal timers in the Atmega (328p in this case for my testing) is that all normal timers are disabled when the CPU goes into the sleep mode and thus would never wake up if were waiting for one of those to tick! Hence why calling millis() will only show how long the CPU has actually been awake – it’s the time that the normal timers were ticking.

Sleeper.h


#ifndef SLEEPER_H
#define SLEEPER_H

#include <arduino.h>
#include <avr/sleep.h>
#include <avr/power.h>

// This sleeper class utilises the watchdog timer as the oscilators for the timers get powered off in SLEEP_MODE_PWR_DOWN
// mode, which is the most power effcient sleep mode. The sleep time will not be more than 16ms accurate.

class Sleeper {
    public:
    void SleepMillis(long millis);

    private:
    void DoSleep();
    void SetupWatchdog(uint8_t prescalar);

    // Watchdog Prescalars
    const int static NumberOfPrescalars = 10;
    uint8_t Prescalars[NumberOfPrescalars] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
    long Times[NumberOfPrescalars] = {8000, 4000, 2000, 1000, 500, 250, 128, 64, 32, 16};
};

#endif



Sleeper.cpp


#include "Sleeper.h"

   // The interupt vector for the watch dog must be present, even if empty else the CPU resets.
ISR(WDT_vect) {
    // Do nothing.
}

/// Sleeps the arduino for a number of milliseconds
void Sleeper::SleepMillis(long millis) {

    // OPTIONAL delay to wait for all things to finish, e.g. serial prints - else you may get garbled serial prints from sleeping before the sending has finished.
    //delay(50);
 
    uint8_t prescalar = 0;

    // Sleep for the longest possible watchdog timeout that's less than millis and keep going until there are no millis left.
    while (millis > Times[NumberOfPrescalars-1]) {
        for (int i = 0; i < NumberOfPrescalars; ++i) {
            if (millis > Times[i]) {
                prescalar = Prescalars[i];
                millis -= Times[i];
                break;
            }
        }
        SetupWatchdog(prescalar); 
        DoSleep();
    }
}

/// Sets up the watchdog to timeout after a certain time period.
/// There are many comments / notes in this function which have been copied directly from the data sheet for
/// user convenience.
void Sleeper::SetupWatchdog(uint8_t prescalar) {

    // Prescalars can be: 0=16ms, 1=32ms, 2=64ms, 3=125ms, 4=250ms, 5=500ms, 6=1sec, 7=2sec, 8=4sec, 9=8sec
    if (prescalar > 9) {
        prescalar = 9;
    }

    // _BV is a macro that can be thought of as a funtion that takes in a number and outputs a byte with that bit set.
 
    // WDTCSR - Watchdog Timer Control Register
    // Note that WDP[0-3] is not in order (WDP[0-2] is but WDP3 is actually bit 5 not 3!) so we have to preprocess the prescaler passed in above.
    // bits 7 = WDIF, 6 = WDIE, 5 = WDP3, 4 = WDCE, 3 = WDE, 2 = WDP2, 1 = WDP1, 0 = WDP0
 
    // WDP3 WDP2 WDP1 WDP0 Typical Time-out at VCC = 5.0V
    // 0 0 0 0 16ms
    // 0 0 0 1 32ms
    // 0 0 1 0 64ms
    // 0 0 1 1 0.125 s
    // 0 1 0 0 0.25 s
    // 0 1 0 1 0.5 s
    // 0 1 1 0 1.0 s
    // 0 1 1 1 2.0 s
    // 1 0 0 0 4.0 s
    // 1 0 0 1 8.0 s
 
    // Take the first 3 bits (WDP[0-2])
    uint8_t wdtPrescalarBits = prescalar & 7;

    // Now we need to set WDP3, to do this we don't set bit 3 but bit 5, so if our presclar had bit 8 set i.e. it 
    // was 8 or 9 being passed in then we must set WDP3 accordingly, else we could have just used prescar as it was passed in. 
    if ( prescalar & 8 ) {
        wdtPrescalarBits |= _BV(WDP3);
    }
 
    // MCUSR – MCU Status Register
    // The MCU Status Register provides information on which reset source caused an MCU reset.
    // MCUSR Bit 3 – WDRF: Watchdog System Reset Flag
    // This bit is set if a Watchdog System Reset occurs. The bit is reset by a Power-on Reset, or by writing a logic zero to the flag.
    MCUSR &= ~_BV(WDRF);
 
    // WDTCSR Bit 4 – WDCE: Watchdog Change Enable
    // This bit is used in timed sequences for changing WDE and prescaler bits. To clear the WDE bit, and/or change the prescaler bits, WDCE must be set.
    // Once written to one, hardware will clear WDCE after four clock cycles.
 
    // WDTCSR Bit 3 – WDE: Watchdog System Reset Enable
    // WDE is overridden by WDRF in MCUSR. This means that WDE is always set when WDRF is set. To clear
    // WDE, WDRF must be cleared first. This feature ensures multiple resets during conditions causing failure, and a
    // safe start-up after the failure

    // Allow changes
    WDTCSR = _BV(WDCE) | _BV(WDE);

    // WDTCSR Bit 6 – WDIE: Watchdog Interrupt Enable
    // When this bit is written to one and the I-bit in the Status Register is set, the Watchdog Interrupt is enabled. If WDE is cleared in combination with this setting, the Watchdog Timer is in Interrupt Mode, and the corresponding interrupt is executed if time-out in the Watchdog Timer occurs. If WDE is set, the Watchdog Timer is in Interrupt and System Reset Mode. The first time-out in the Watchdog Timer will set WDIF. Executing the corresponding interrupt vector will clear WDIE and WDIF automatically by hardware (the Watchdog goes to System Reset Mode). This is useful for keeping the Watchdog Timer security while using the interrupt. To stay in Interrupt and System Reset Mode, WDIE must be set after each interrupt. This should however not be done within the interrupt service routine itself, as this might compromise the safety-function of the Watchdog System Reset mode. If the interrupt is not executed before the next time-out, a System Reset will be applied.
    // Note: 1. WDTON Fuse set to "0" means programmed and "1" means unprogrammed.

    // Watchdog Timer Configuration
    //WDTON WDE WDIE Mode Action on Time-out
    //1 0 0 Stopped None
    //1 0 1 Interrupt Mode Interrupt
    //1 1 0 System Reset Mode Reset
    //1 1 1 Interrupt and System Reset Mode Interrupt, then go to System Reset Mode
    //0 x x System Reset Mode Reset
 
    // Perform the change.
    WDTCSR = _BV(WDCE) | wdtPrescalarBits | _BV(WDIE);
}

/// Powers down system.
void Sleeper::DoSleep() {

    // Set the sleep mode.
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); 
    sleep_enable();

    // Put the device into sleep mode.
    sleep_mode();

    // System continues execution here after watchdog timeout.
    sleep_disable();
}

ESP8266 NodeMCU 0.9 Arduino C++

A friend from work let me borrow an ESP8266 module on a NodeMCU board and I went about trying to get started with it and thought I’d write up my notes. (using C++)

nodemcu

To make this easier I’m going to use the easily available Arduino IDE and a set of libraries for the ESP8266 module.

Install Arduino IDE

Arduino download page.

Add libraries

Using these libraries you can follow the steps below to enable your Arduino IDE to work with many ESP8266 modules. https://github.com/esp8266/Arduino

Using the Arduino IDE Boards Manager:

  • Start Arduino and open Preferences window.
  • Enter http://arduino.esp8266.com/stable/package_esp8266com_index.json into Additional Board Manager URLs field. You can add multiple URLs, separating them with commas.
  • Open Boards Manager from Tools > Board menu and install esp8266 platform.
  • Select your ESP8266 board from Tools > Board menu.

 

Code for the ESP8266 server

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

const char* ssid = "YourWifiName";
const char* password = "YourWifiPassword";

void HandleRoot();
void HandleCommand();
void FlashLeds();
void TurnOffLeds();
void HandleNotFound();


ESP8266WebServer server(80);

// what to do when a client requests "http://<IP>"
void HandleRoot() {
    server.send(200, "text/plain", "hello from esp8266!"); 
}

// What to do when a client requests "http://<IP>/command"
// This function doesn't care whether it is a GET or POST at the moment, 
// it will treat them the same just to make things easier.
void HandleCommand() {
    for (uint8_t i=0; i<server.args(); i++) {
        if (server.argName(i) == "flash") {
            if (server.arg(i) == "on") {
                FlashLeds();
                server.send(200, "text/plain", "LEDs turned on");
            } else {
                TurnOffLeds();
                server.send(200, "text/plain", "LEDs turned off");
            }
        }
    }
    server.send(404, "text/plain", "Command not found");
}

void FlashLeds() {
    // Flash LEDs or something then exit this func so that the webserver carries on running.
    Serial.println("LEDs on.");
}

void TurnOffLeds() {
    // Flash LEDs or something then exit this func so that the webserver carries on running.
    Serial.println("LEDs off.");
}

// If the route is not found then default to printing out what was sent to this server to aid in debug.
void HandleNotFound() {
    String message = "File Not Found\n\n";
    message += "URI: ";
    message += server.uri();
    message += "\nMethod: ";
    message += (server.method() == HTTP_GET)?"GET":"POST";
    message += "\nArguments: ";
    message += server.args();
    message += "\n";
    for (uint8_t i=0; i<server.args(); i++) {
        message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
    }
    server.send(404, "text/plain", message);}

void setup(void) {
    Serial.begin(115200);
    WiFi.begin(ssid, password);
    Serial.println("Setting up.");

    // Wait for connection
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println(".");
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    if (MDNS.begin("esp8266")) {
        Serial.println("MDNS responder started");
    }

    /* 
    //I left this in commented out just to show this method as well.
    server.on("/inline", []() {
        server.send(200, "text/plain", "this works as well");
    });
    */
 
    // Setup server routes.
    server.on("/", HandleRoot);
    server.on("/command", HandleCommand);
    server.onNotFound(HandleNotFound);
    server.begin();
    Serial.println("HTTP server started");
}

void loop(void) {
    server.handleClient();
}

 

Code for webpage to send commands

The below code can be copied and pasted into a text file and then save the file as yourfilename.html, it will allow you to send commands to the module / server.

The IP address of my module is 192.168.1.115 so make sure you replace that IP in the below code with the one of your module.

Also note that you could actually serve this page straight from the module itself and not have  separate file.

<head>
    <!--<script src="jquery.js"></script>-->
    <script src="http://code.jquery.com/jquery-2.2.1.min.js "></script>

 <script>
    $(function () {

    // Bind the click of the button to the function that posts data to the server.
    $('#buttonFlashOn').click( function() {
        $.post("http://192.168.1.115/command", { flash : "on"})
         .done(function( data ) {
             //alert( "Data returned: " + data );
        });
    });

    // bind the click of the button to the function that posts data to the server.
    $('#buttonFlashOff').click( function() {
        $.post("http://192.168.1.115/command", { flash : "off"})
         .done(function( data ) {
            //alert( "Data returned: " + data );
          });
    });

});
</script>
 
 
</head>

<body>
 <br/>
 <button id="buttonFlashOn">Flash on</button> 
 <br/>
 <br/>
 <button id="buttonFlashOff">Flash off</button>
 <br/>
</body>

enjoy 🙂

Decent and cheap hosting & domains based in the UK

I’ve always had good experiences with TSO Host so I thought I’d suggest them as a good place to get started on your own web development.

They’re UK based (great for me) and all their tech support staff are as well, which is great. I’ve added a referral link above for anyone that wants to use them and possibly help me out too.

Hope that made someone’s hosting search a little easier 🙂

Laravel routes not working

If your main Laravel page is working but no other routes are then this may be of use to you.

My doc root is here:

/var/www/html/quickstart/public

Firstly enable mod rewrite:

sudo a2enmod rewrite

Then check your routes that weren’t loading, if it works great! If not, then read on.

Edit your default apache site conf file:

sudo gedit /etc/apache2/sites-enabled/000-default.conf

Mine is below:

<VirtualHost *:80>
    ServerName localhost
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/quickstart/public
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    <Directory "/var/www/html/quickstart/public">
        Allowoverride All
    </Directory>
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

That worked for me on Ubuntu, hope it helps someone.

I have been trying out laravel and intend to use it for some projects in the future but I’ve been finding it very frustrating to get it installed and running. The homstead vagrant setup they have just simply doesn’t work and this is the second time I’ve tried it over the past 3 months, so it wasn’t a passing bug. I’ve only ever got laravel up and running on Linux by setting it up manually and never got it working on windows. Hopefully after a few fresh installs I’ll have it down to a few nice steps and I’ll write those up =)