• Putting an idle WiFi router to use as a LAN-WLAN bridge

    05/29/2020 at 14:43 0 comments

    In the current situation I have time to do the little chores I promised myself to do.

    One was to set up an unused WiFi router as an auxiliary access point. In parts of my abode, the WiFi signal from the main router isn't strong, and besides it only handles the older standard. I have a spare router which handles the latest WiFI standards which I decided to put to use.

    You may ask at this point why not swap the unused router in? Two reasons, the old router has DD-WRT on it and I have a particular set up for it, and the new router has gigabit Ethernet ports so I can use it as an Ethernet switch at the same time, and not have to run another Cat-6 cable to that area. Finally I use the Internet more from the new location.

    Note that this is not about setting up a WiFi relay. You can buy such devices off the shelf to boost the signal. This is using a spare WiFi router as a LAN-WLAN bridge so you need to have wired Ethernet already. It also doesn't matter what the front end of the router is, whether ADSL or cable, as we don't use it at all. So ignore any Ethernet ports that say WAN or Internet. We only use the ones that say LAN or Ethernet. Here's a typical back panel. We want the yellow interfaces.


    These are the steps you need to do:

    1. Reset the router to factory settings.
    2. Connect to it with a computer, usually a laptop, at the factory IP address and login.
    3. Disable the DHCP server of the router.
    4. Change the LAN address of the router to a free IP address in your subnet that isn't in the dynamic range of the main DHCP server.
    5. Reconnect to the router at the new address.
    6. Set up the WiFi using the same SSID and passwords as your main router.
    7. Check that WiFi clients connect to your router.
    8. Backup the configuration to a file on your computer.

    Let's go through each of the steps.

    1. Reset the router to factory settings

    Usually this is done by poking a paper clip into a recessed reset button on the back or bottom of the router.

    2. Connect to it with a computer, usually a laptop, at the factory IP address and login

    Connect with an Ethernet patch cable and access the web interface. You need to know the factory default IP address and login/password. The manual will tell you or it may be on a sticker on the router. Often the address iis 192.168.0.1. You could either let the laptop get an address by DHCP, but since you will need to change it in step 5, you might as well learn how to configure it to a static address. Change the admin password after you have logged in.

    3. Disable the DHCP server of the router

    Somewhere in the settings, probably advanced, is where you turn off the DHCP server of the router. This is so that it won't clash with the main router. In my case it wasn't sufficient to disable it. I also had to shrink the range to a small one or it would say illegal address at the next step.

    4. Change the LAN address of the router

    You need to assign the router a fixed address that is on your LAN but not in the DHCP range of the main router. Say your main router gives out addresses from 192.168.100.100 to 192.168.200.200. In this example .99 or .201 is available. After you have changed this, your laptop cannot reach the router, so proceed to:

    5. Reconnect to the router at the new address

    For this you can either change the IP address of your laptop's Ethernet interface, or switch to the main router's WiFi which will be on the same subnet. For the former you would take another address on your LAN, say 192.168.100.202. The router address you give to your browser is for the last example: http://192.168.100.201

    6. Set up the WiFI using the same SSID and passwords as your main router

    Otherwise your devices will be confused which one to log onto. Naturally you should use the same standard, which is usually WPA2-AES. At the point you may wish to choose the wireless channel for least overlap with your neighbours, but if you are close enough to the router, it's not as important as your signal will be stronger. For this tuning task...

    Read more »

  • A lazy person avoided installing the AVR-gcc toolchain

    05/28/2020 at 10:28 0 comments

    As all my friends know, my mugshot is used to illustrate the word lazy in dictionaries. 😉

    Anyway I found this AVR MCU in my junkspares box and wanted to test it out. I quickly ascertained that I should install the AVR-gcc toolchain. Work! Four-letter word!

    Wait a moment, isn't the toolchain already included in the Arduino IDE which I already have? I didn't want to use the IDE for bare metal programming because it assumes a bootloader to transfer to a boilerplate main() that calls setup() and loop(), a programmer like an Arduino board, and a definition for your MCU's board. (But if you want to go that way, look into MiniCore.) I just wanted the tools. A quick check of the contents of my IDE installation showed that they lived under /opt/arduino-1.8.12/hardware/tools/avr on my Linux machine, and the binaries were under bin/.

    So, first I make a symbolic link

    $ ln -s /opt/arduino-1.8.12 /opt/arduino

    and use /opt/arduino for other symlinks so that when I upgrade I only have to shift the arduino link and not redo all the other symlinks.

    Now there are two ways to make the executables available, add /opt/arduino/hardware/tools/avr/bin to $PATH, or make symlinks from another PATH directory to them. I used the latter.

    $ cd /usr/local/bin
    $ ln -s /opt/arduino/hardware/tools/avr/bin/* .

    and checked that it works

    $ which avr-gcc
    /usr/local/bin/avr-gcc

    Setting that up only took a few minutes. By the way you probably need to use sudo or su to do some of those things.

    Now to write a very special program, blink.c: 😉

    #define __AVR_AT90S8515__       1
    
    #include        <avr/io.h>
    #include        <util/delay.h>
    
    void delay(unsigned int ms)
    {
        while (--ms)
            _delay_ms(1.0);
    }
    
    int main(void) {
        PORTB = 0;
        DDRB = 0xFF; // LED bank
    
        while (1) {
            PORTB = 0x55;
            delay(500);
            PORTB = 0xAA;
            delay(500);
        }
    }

    The Makefile that controls the build:

    CC=avr-gcc
    F_CPU=4000000
    DEBUG=
    CFLAGS=-Wall $(DEBUG) -Os -DF_CPU=$(F_CPU)
    INCLUDES=
    AVRSIZE=avr-size
    OBJCOPY=avr-objcopy
    
    blink.hex:      blink.bin
    
    blink.bin:      blink.c Makefile
    
    %.s:            %.c
            $(CC) $(CFLAGS) $(INCLUDES) -S $<
    
    %.bin:          %.c
            $(CC) $(CFLAGS) $(INCLUDES) $< -o $(<:.c=.bin)
            $(AVRSIZE) -C $(<:.c=.bin)
    
    %.hex:          %.bin
            $(OBJCOPY) -j .text -j .data -O ihex $< $(<:.bin=.hex)
    
    clean:
            rm -f *.s *.bin *.hex

    As it turned out I didn't need any -I options because the Arduino toolchain implicitly includes .h files from /opt/arduino/hardware/tools/avr/include. All you have to do is define the MCU type before the #include <avr/io.h>. F_CPU is defined to be the crystal frequency for correct delays. Notice that there is no flash rule that calls avrdude. This because I will be flashing the program with my programmer.

    And here is the resulting blink.s:

            .file   "blink.c"
    __SP_H__ = 0x3e
    __SP_L__ = 0x3d
    __SREG__ = 0x3f
    __tmp_reg__ = 0
    __zero_reg__ = 1
            .text
    .global delay
            .type   delay, @function
    delay:
    /* prologue: function */
    /* frame size = 0 */
    /* stack size = 0 */
    .L__stack_usage = 0
    .L2:
            sbiw r24,1
            brne .L3
    /* epilogue start */
            ret
    .L3:
            ldi r30,lo8(999)
            ldi r31,hi8(999)
    1:      sbiw r30,1
            brne 1b
            rjmp .
            nop
            rjmp .L2
            .size   delay, .-delay
            .section        .text.startup,"ax",@progbits
    .global main
            .type   main, @function
    main:
    /* prologue: function */
    /* frame size = 0 */
    /* stack size = 0 */
    .L__stack_usage = 0
            out 0x18,__zero_reg__
            ldi r24,lo8(-1)
            out 0x17,r24
            ldi r29,lo8(85)
            ldi r28,lo8(-86)
    .L5:
            out 0x18,r29
            ldi r24,lo8(-12)
            ldi r25,lo8(1)
            rcall delay
            out 0x18,r28
            ldi r24,lo8(-12)
            ldi r25,lo8(1)
            rcall delay
            rjmp .L5
            .size   main, .-main
            .ident  "GCC: (GNU) 7.3.0"

    I flashed this to the MCU with the programmer. Unfortunately when I put it on the development board described in #Adventures with a STC89C52 development board nothing happened. Maybe the chip is bad, or maybe the board isn't suitable for it. I did at first make the mistake of using the onboard 11.0592 MHz crystal until I realised that was out of the specifications of the AVR MCU I had, but substituting a slower crystal didn't...

    Read more »

  • Hacking on Ted's LED clock software

    05/21/2020 at 04:42 4 comments

    @Paul Gallagher recently did a respin, or should that be a re-ring, of @Ted Yapo 's 20 year old clock project that used a PIC16F84. I thought the original and the tribute were fantastic projects and was curious about the software. As expected it's very compact PIC assembler. I wondered whether it could be re-expressed in C, mainly as an excuse for me to get familiar with the PIC16 architecture and the MPLAB X IDE, but also to perhaps implement in future with a different MCU. Hence this quick project.

    The original clock.asm is 299 lines, consisting of a main routine, a couple of utility routines and an interrupt routine which is run 32 times per second to update the display and handle the buttons. After setting up the registers, the main routine just loops indefinitely and all the work is done by the interrupt routine. So it's nearly all straight line code.

    The assembler was fairly easy to translate but the branches gave me a bit of headache until I defined a handy macro. I ended up with a clock.c of 114 lines, more readable of course.

    I wanted to know what the penalty was in terms of code size. According to the map generated by the assembler, 163 words were used. The C version required 261 words. Comparing the assembler code I found several reasons for this. One is that a more general table lookup routine for the hand to port bit translation is used, one that apparently caters to large tables. There is also some C startup code. The compiler generates longer sequences in some cases. For example an increment of a variable, for example tick, was this in the original.

            INCF    tick

    but the C compiler generated this:

            movlw   1
            movwf   ??_tc_int
            movf    ??_tc_int,w
            addwf   _tick,f
    

    Not sure why it had to do those extra moves.

    Finally the C compiler didn't know that main goes into a loop, so there is no need to save context on entering an interrupt and restore on exit. Of course these days 100 words is nothing in the scheme of thngs for MCUs. Except for the context save and restore, the interrupt routine should run at the same speed. At 32 Hz, it doesn't matter anyway.

    A few things worth mentioning. The free xc8 compiler will only do -O2. You need to pay for higher levels. Also the free version won't generate an assembler file, even if you tick the box in the config, but you can observe the xc8-cc command that it runs to generate an object and then substitute -S for -c, or add it in the command. I did not need the register bank switching macros as xc8 knows to insert them when needed.

    Microchip recommends that interrupt functions be kept small so that less or no context has to be saved. To achieve this, you have to convert this pattern:

    void __interrupt() tc_int(void)
    {
        lots of work
        ...
        T0IF = 0;
    }
    ...
    void main(void)
    {
        startup initialisation
        ...
        while (1)
            ;
    }

    to:

    unsigned char flag;
    
    void __interrupt() tc_int(void)
    {
        flag = 1;
        T0IF = 0;
    }
    ...
    void main(void)
    {
        startup initialisation
        ...
        while (1) {
            flag = 0;
            while (!flag)
                ;
            lots of work
            ...
        }
    }

    Anyway here is the C code in its entirety for interest. The #pragmas for the configuration bits can be generated with a tool in the IDE. I haven't checked that the branches are correct but they should be easy to fix if I ever build one. The 16C84 is overpriced now for its capability as it is used for repairing old equipment. A more modern member of the family might be cheaper.

    // Clock I driver - LED clock
    // Copyright (C) 2000, Theodore C. Yapo.  All rights reserved.
    
    // CONFIG
    #pragma config FOSC = LP        // Oscillator Selection bits (LP oscillator)
    #pragma config WDTE = ON        // Watchdog Timer (WDT enabled)
    #pragma config PWRTE = OFF      // Power-up Timer Enable bit (Power-up Timer is disabled)
    #pragma config CP = OFF         // Code Protection bit (Code protection disabled)
    
    #include <xc.h>
    
    #define REGBANK_0       RP0 = 0
    #define REGBANK_1       RP0 = 1
    #define IS_UP(x)        (x == 1)
    
    unsigned char tick, hour, minute_1, minute_5, second_1, second_5, porta_value, portb_value, minutes_button,...
    Read more »