• Content count

  • Joined

  • Last visited

  • Days Won


yyrkoon last won the day on May 14

yyrkoon had the most liked content!

About yyrkoon

  • Rank
    Level 4
  • Birthday 07/05/1966

Profile Information

  • Gender
    Not Telling
  • Location

Recent Profile Visitors

2,312 profile views
  1. Simple Line / mux select So, this is a variation on using GPIO, via direct memory access. The benefit to using this code, or similar. Is that there is only two syscalls for each line / mux select. Versus 12 syscalls if using the standard "Linux way" using open(), write(), read(), and close(). That's PER pin. yikes. Keep in mind, I haven't put a ton of thought into this code, so it could very well be rough around the edges, but it does work. So for our particular application. We have a Line select for AIN6 on the beaglebone. As we needed more ADC channels. Again, for our own application, we needed 5 additional channels, with valid cmd line tool only accepting values 0-3, and 7. strtol() is set to base 16, so as not to interpret ASCII characters as the numerical value zero. Or potentially causing a seg fault. Put more correctly, if using base "10" as the numerical base value, values like 0x7, or absurdly large strings of characters could be interpreted as 0, and potentially something else undesirable. #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #define GPIO0 (0x44E07000) #define GPIO2 (0x481AC000) #define GPIO_SIZE (0x2000) #define GPIO_DATAOUT (0x13C) #define GPIO_DATAIN (0x138) #define MA0 (1<<26) /*gpio_0*/ #define MA1 (1<<23) /*gpio_2*/ #define MA2 (1<<24) /*gpio_2*/ void line_select(uint8_t asp_value) { void *gpio_addr; unsigned int *bank0_out, *bank2_out; int fd = open("/dev/mem", O_RDWR); gpio_addr = mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO0); bank0_out = gpio_addr + GPIO_DATAOUT; gpio_addr = mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO2); bank2_out = gpio_addr + GPIO_DATAOUT; close(fd); (asp_value & (1 << 0)) ? (*bank0_out |= MA0) : (*bank0_out &= ~(MA0)); (asp_value & (1 << 1)) ? (*bank2_out |= MA1) : (*bank2_out &= ~(MA1)); (asp_value & (1 << 2)) ? (*bank2_out |= MA2) : (*bank2_out &= ~(MA2)); printf("Line select: %u%u%u\n", !!(*bank2_out & MA2), !!(*bank2_out & MA1), !!(*bank0_out & MA0)); } int main(int argc, char *argv[]) { if(argc != 2) exit(1); int argv2 = strtol(argv[1], NULL, 16); if(argv2 != 7){ if(argv2 < 0 || argv2 > 3) exit(1); } line_select(argv2); return 0; } Output: root@wgd:~/dl-i2c-test# nano tst.c root@wgd:~/dl-i2c-test# gcc -Wall -o tst tst.c root@wgd:~/dl-i2c-test# ./tst 0 Line select: 000 root@wgd:~/dl-i2c-test# ./tst 1 Line select: 001 root@wgd:~/dl-i2c-test# ./tst 2 Line select: 010 root@wgd:~/dl-i2c-test# ./tst 3 Line select: 011 root@wgd:~/dl-i2c-test# ./tst 4 root@wgd:~/dl-i2c-test# ./tst 0x7 Line select: 111 root@wgd:~/dl-i2c-test# ./tst 0xFF root@wgd:~/dl-i2c-test# ./tst -1 root@wgd:~/dl-i2c-test#
  2. For those of you who are interested. I made it easy to find the pin info "spread sheet" info on a new git here: https://github.com/wphermans/bb-info
  3. Working with GPIO, closer to the hardware: In addition to the "traditional Linux" way of working through the file system when using hardware. One can use mmap() and /dev/mem/, when absolute performance is needed, or perhaps if one needs to minimize the load put on the processor when dealing with GPIO hardware. One does need to be aware that when doing so, all operations on the hardware used is done without the kernel knowing. This is kind of a misnomer, as Linux will "eventually" know something is happening, and adjust the sysfs entries accordingly. *Assuming*, Linux even knows such hardware even exists by way of loading the appropriate drivers. Which does not have to be the case. One can essentially write their own "driver" from userspace using this technique. But you may have to set the hardware up manually through it's respective registers. I'll leave further explanation up as an exercise to the reader to figure out. As I do not fully understand the implications, for each hardware module on the AM335x processor myself. GPIO, is easy, but something such as PWM, ADC, or I2C, etc requires more than a simple configuration to get working properly. Anyway, some of this code will probably need further explanation, so I will pick apart some of the code and explain what is happening after the fact. Code: #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #define GPIO0 (0x44E07000) #define GPIO1 (0x4804C000) #define GPIO2 (0x481AC000) #define GPIO3 (0x481AE000) #define GPIO_SIZE (0x2000) #define GPIO_DATAOUT (0x13C) #define GPIO_DATAIN (0x138) #define PWM1 (1<<2) /*gpio_0*/ #define PWM2 (1<<3) /*gpio_0*/ #define PWM3 (1<<18) /*gpio_1*/ #define PWM4 (1<<22) /*gpio_0*/ #define PWM5 (1<<19) /*gpio_1*/ #define PWM6 (1<<23) /*gpio_0*/ #define Z1IN (1<<12) /*gpio_1*/ #define Z2IN (1<<13) /*gpio_1*/ #define Z3IN (1<<14) /*gpio_1*/ #define Z4IN (1<<15) /*gpio_1*/ #define Z5IN (1<<16) /*gpio_1*/ #define Z6IN (1<<17) /*gpio_1*/ int main(int argc, char *argv[]) { void *gpio_addr; unsigned int *gpio0_out; unsigned int *gpio1_out, *gpio1_in; int fd = open("/dev/mem", O_RDWR); gpio_addr = mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO0); gpio0_out = gpio_addr + GPIO_DATAOUT; gpio_addr = mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1); gpio1_out = gpio_addr + GPIO_DATAOUT; gpio1_in = gpio_addr + GPIO_DATAIN; close(fd); *gpio0_out |= PWM1 + PWM2 + PWM4 + PWM6; *gpio1_out |= PWM3 + PWM5; usleep(1000); printf("PWM1 %u Z1IN %u\n", !!(*gpio0_out & PWM1), !!(*gpio1_in & Z1IN)); printf("PWM2 %u Z2IN %u\n", !!(*gpio0_out & PWM2), !!(*gpio1_in & Z2IN)); printf("PWM3 %u Z3IN %u\n", !!(*gpio1_out & PWM3), !!(*gpio1_in & Z3IN)); printf("PWM4 %u Z4IN %u\n", !!(*gpio0_out & PWM4), !!(*gpio1_in & Z4IN)); printf("PWM5 %u Z5IN %u\n", !!(*gpio1_out & PWM5), !!(*gpio1_in & Z5IN)); printf("PWM6 %u Z6IN %u\n", !!(*gpio0_out & PWM6), !!(*gpio1_in & Z6IN)); printf("\n"); *gpio0_out &= ~(PWM1 + PWM2 + PWM4 + PWM6); *gpio1_out &= ~(PWM3 + PWM5); usleep(1000); printf("PWM1 %u Z1IN %u\n", !!(*gpio0_out & PWM1), !!(*gpio1_in & Z1IN)); printf("PWM2 %u Z2IN %u\n", !!(*gpio0_out & PWM2), !!(*gpio1_in & Z2IN)); printf("PWM3 %u Z3IN %u\n", !!(*gpio1_out & PWM3), !!(*gpio1_in & Z3IN)); printf("PWM4 %u Z4IN %u\n", !!(*gpio0_out & PWM4), !!(*gpio1_in & Z4IN)); printf("PWM5 %u Z5IN %u\n", !!(*gpio1_out & PWM5), !!(*gpio1_in & Z5IN)); printf("PWM6 %u Z6IN %u\n", !!(*gpio0_out & PWM6), !!(*gpio1_in & Z6IN)); return 0; } Output: root@wgd:~/dl-i2c-test# gcc -Wall -o tst tst.c root@wgd:~/dl-i2c-test# ./tst PWM1 1 Z1IN 0 PWM2 1 Z2IN 0 PWM3 1 Z3IN 0 PWM4 1 Z4IN 0 PWM5 1 Z5IN 0 PWM6 1 Z6IN 0 PWM1 0 Z1IN 1 PWM2 0 Z2IN 1 PWM3 0 Z3IN 1 PWM4 0 Z4IN 1 PWM5 0 Z5IN 1 PWM6 0 Z6IN 1 So the first thing to understand about this code is that it will return the output immediately after the command is issued. Partly this has to do with using sleep() in the first example, and usleep() in this example. 1 Second sleep, versus a 1000 uSec( 1 millisecond ) sleep. I will say however, that the timing cut off for reading from a GPI with the current kernel I'm using ( which is RT PREEMPT by the way ) is between 500 uSec, and 1000 uSec. Exactly where, I'm not sure, but I've tested all the way down to 10 uSec, and the slew rate of the GPI's at this speed are far too slow. e.g. I get back incorrect readings, even though visual inspection( LEDs ) shows everything working correctly. Now it may be possible to speed up the sysfs method by using usleep() instead, but be aware there would be ~5 syscalls per pin group, and these calls would hinder performance greatly when compared to this method. Now a bit of code explanation: #define GPIO0 (0x44E07000) #define GPIO1 (0x4804C000) #define GPIO2 (0x481AC000) #define GPIO3 (0x481AE000) The base address for each GPIO bank. So how do "We" know these addresses ? Well, you can read the TRM for the AM355x processor, and get this information. But there is an easier way. root@wgd:~/dl-i2c-test# ls /sys/devices/platform/ocp/*.gpio/gpio/ /sys/devices/platform/ocp/44e07000.gpio/gpio/: gpio2 gpio22 gpio23 gpio26 gpio3 gpiochip0 /sys/devices/platform/ocp/4804c000.gpio/gpio/: gpio44 gpio45 gpio46 gpio47 gpio48 gpio49 gpio50 gpio51 gpio60 gpiochip32 /sys/devices/platform/ocp/481ac000.gpio/gpio/: gpio86 gpio87 gpio88 gpiochip64 /sys/devices/platform/ocp/481ae000.gpio/gpio/: gpio110 gpio111 gpio112 gpio115 gpio117 gpiochip96 Now, not only this easier to figure out the base addresses for each GPIO bank. But if like me you do not memorize each gpioxx number, this will tell you which GPIO bank each pin is on. When using a device tree overlay file to configure your pins. Very handy. As glancing at this is much easier than parsing a spread sheet when you're doing this: #define PWM1 (1<<2) /*gpio_0*/ #define PWM2 (1<<3) /*gpio_0*/ #define PWM3 (1<<18) /*gpio_1*/ #define PWM4 (1<<22) /*gpio_0*/ #define PWM5 (1<<19) /*gpio_1*/ #define PWM6 (1<<23) /*gpio_0*/ #define Z1IN (1<<12) /*gpio_1*/ #define Z2IN (1<<13) /*gpio_1*/ #define Z3IN (1<<14) /*gpio_1*/ #define Z4IN (1<<15) /*gpio_1*/ #define Z5IN (1<<16) /*gpio_1*/ #define Z6IN (1<<17) /*gpio_1*/ These numbers: #define GPIO_SIZE (0x2000) #define GPIO_DATAOUT (0x13C) #define GPIO_DATAIN (0x138) You will have to read the TRM, to figure out, if you need to be absolutely certain. However know that the page_file size in Linux will always be a multiple of 4k, and I just so happen to know off the top of my head that each GPIO bank "width" is 8192 bytes. To store all the GPIO configuration registers. Or PAGE_FILE_SIZE * 2. Now onto something that many people may not know. printf("PWM1 %u Z1IN %u\n", !!(*gpio0_out & PWM1), !!(*gpio1_in & Z1IN)); !! in this case is not one, but actually two operators. Basically NOT NOT, which essentially works as a shorthand for a ternary conditional check. Returning a value of 0, or 1. Kind of a hack, that is definitely not self explanatory at first glace.Until you work it out in your head what is actually going on. Then, it's something that becomes obvious. I'd argue that this short hand form is actually as readable as a "proper" ternary conditional check.
  4. Working with GPIO: So where to start. GPIO is probably one of the most easiest things to work with on a beaglebone. Once you understand a few things. First, the kernel through sysfs has no idea what pins are tied to which header pin. So one needs to find a spread sheet that will explain which header pin is attached to which GPIO pin through the sysfs file structure. The base path for the sysfs gpio path is /sys.class/gpio/. However the explanation of how all this works is fairly lengthy, and there are a lot of guides dating back to the original bealgebone(white) as to how all this works. In short, if you need to know this information I suggest you search the web for information. This is the "manual" way of setting up GPIO. Another alternative is again to use a device tree overlay file. Which in of it's self is initially a lengthy process to understand. In short: https://github.com/beagleboard/bb.org-overlays/blob/master/src/arm/univ-nhdmi-00A0.dts#L232-#L234 The highlighted text here is defining a pin number, and the mode that pin needs to be set into for GPIO operation, with pullup, and RxActive enabled. These files for the purpose of this explanation are not exactly the best to use. Perhaps at some point in the future Ill create a simple overlay file, for the purpose of better explanation. As it is, this particular overlay is for Universal IO, and it covers most, if not all pins, with most if not all possible modes for each pin. Used with config-pin this allows one to configure each pin as needed dynamically from the command line . . . So onto some simple code. First, again, some explanation is required. For our board we have some PWM pins, and some input pins, among other things. Both PWM, and input only use 6 pins each. In order to test the circuitry of our boards, To test the our capes circuitry, we instead use the PWM pins as GPO's, the input pins as GPI's, and have a test header connecting each "zone" together from GPO to GPI. Additionally, there is an LED connected to each GPO on this test header for visual inspection. Code: #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #define HIGH 1 #define LOW 0 #define PWM1 2 #define PWM2 3 #define PWM3 50 #define PWM4 22 #define PWM5 51 #define PWM6 23 #define Z1IN 44 #define Z2IN 45 #define Z3IN 46 #define Z4IN 47 #define Z5IN 48 #define Z6IN 49 void set_pin(int pin_num, int value) { char gpio_path[40] = {0}; char str_value[2] = {0}; sprintf(str_value, "%d", value); sprintf(gpio_path, "/sys/class/gpio/gpio%d/value", pin_num); int fd = open(gpio_path, O_WRONLY); if(fd == -1){ perror(gpio_path); exit(1); } int nread = write(fd, str_value, 1); if( nread < 0){ perror("write()"); exit(1); } close(fd); } int get_pin(int pin_num) { char gpio_path[40] = {0}; char str_value[2] = {0}; sprintf(gpio_path, "/sys/class/gpio/gpio%d/value", pin_num); int fd = open(gpio_path, O_RDONLY); if(fd == -1){ perror(gpio_path); exit(1); } read(fd, str_value, sizeof(str_value - 1)); close(fd); return strtol(str_value, NULL, 10); } int main() { set_pin(PWM1, LOW); set_pin(PWM2, LOW); set_pin(PWM3, LOW); set_pin(PWM4, LOW); set_pin(PWM5, LOW); set_pin(PWM6, LOW); sleep(1); printf("PWM1: %i Z1IN: %i \n", get_pin(PWM1), get_pin(Z1IN)); printf("PWM2: %i Z2IN: %i \n", get_pin(PWM2), get_pin(Z2IN)); printf("PWM3: %i Z3IN: %i \n", get_pin(PWM3), get_pin(Z3IN)); printf("PWM4: %i Z4IN: %i \n", get_pin(PWM4), get_pin(Z4IN)); printf("PWM5: %i Z5IN: %i \n", get_pin(PWM5), get_pin(Z5IN)); printf("PWM6: %i Z6IN: %i \n", get_pin(PWM6), get_pin(Z6IN)); printf("\n"); /***********************************************************/ set_pin(PWM1, HIGH); set_pin(PWM2, HIGH); set_pin(PWM3, HIGH); set_pin(PWM4, HIGH); set_pin(PWM5, HIGH); set_pin(PWM6, HIGH); sleep(1); printf("PWM1: %i Z1IN: %i \n", get_pin(PWM1), get_pin(Z1IN)); printf("PWM2: %i Z2IN: %i \n", get_pin(PWM2), get_pin(Z2IN)); printf("PWM3: %i Z3IN: %i \n", get_pin(PWM3), get_pin(Z3IN)); printf("PWM4: %i Z4IN: %i \n", get_pin(PWM4), get_pin(Z4IN)); printf("PWM5: %i Z5IN: %i \n", get_pin(PWM5), get_pin(Z5IN)); printf("PWM6: %i Z6IN: %i \n", get_pin(PWM6), get_pin(Z6IN)); return 0; } Output: root@wgd:~/dl-i2c-test# gcc -Wall -o read_zonein read_zonein.c root@wgd:~/dl-i2c-test# ./read_zonein PWM1: 0 Z1IN: 1 PWM2: 0 Z2IN: 1 PWM3: 0 Z3IN: 1 PWM4: 0 Z4IN: 1 PWM5: 0 Z5IN: 1 PWM6: 0 Z6IN: 1 PWM1: 1 Z1IN: 0 PWM2: 1 Z2IN: 0 PWM3: 1 Z3IN: 0 PWM4: 1 Z4IN: 0 PWM5: 1 Z5IN: 0 PWM6: 1 Z6IN: 0 As one can see the logic between inputs, and output is reversed. This is of course intentional.
  5. I added a couple pictures here: https://plus.google.com/u/0/106867156582775247949 with a short explanation of each. It's much easier for me to just make post on my google+ page and then link back here. I am intentionally going slow with my project here. I want to make sure it all works out good. As it is, I will need to trim the aliminum tray part I ordered precut, to the mini ITX motherboard standard. After that, I'll also have to take measurements of the board, and mark mounting holes. Also from the mini ITX motherboard standard. EDIT: For those of you who also may be interested in the mini ITX specification: http://www.formfactors.org/developer/specs/mini_itx_spec_v1_1.pdf The PDF also covers the mATX, and ATX standards if I'm not mistaken.
  6. How to read an ADC So this bit may seem a little odd to some. Hell I know the hardware, and wrote this really quick snippet as a demonstration, and I think it's odd. The short story here. Is that we have a pin multiplexer on AIN6, and this multiplexer selects an external channel based on a bit pattern sent to it through 3 GPIO pins. That code I won't be showing in this post. but I wanted to point out why I have an odd "mvolts" value. Which indicates maximum input voltage before the voltage limiting resistor network. e.g. the on board ADC pins can only handle 1.8v absolute maximum voltage, and I'm pretty sure without all circuitry voltage drop, outside maximum voltage is supposed to be 0-10v, and wulf probably designed the voltage into the ADC it's self to be less than 1.5v . . . So I started with 10v in mind, saw the reading was definitely too high, and then played a guessing game until the voltage I read from a volt meter, matched what I was reading through the ADC. So there is definite resolution loss here . . . Anyway, the best and easiest way to load drivers for the ADC is to load the stock ADC overlay from /lib/firmware/. root@wgd:~# ls /lib/firmware/ |grep ADC BB-ADC-00A0.dtbo That is the file to load. Which can be loaded through capemgr via the command line manually, or from /boot/uEnv.txt at boot. ADC C code: #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> const char *ain6 = "/sys/bus/iio/devices/iio:device0/in_voltage6_raw"; int main() { int fd; int len; char adc[5] = {0}; float madc = 4095.0f; float mvolts = 7.7f; float bvolts = (mvolts / madc); fd = open(ain6, O_RDONLY); if(fd == -1){ perror("ain6"); exit(1); } len = read(fd, adc, sizeof(adc - 1)); int adc_val = strtol(adc, NULL, 10); float voltage = adc_val * bvolts; printf("%f \n", voltage); close(fd); return 0; } Output: root@wgd:~# gcc -Wall -o adc adc.c adc.c: In function 'main': adc.c:14:6: warning: variable 'len' set but not used [-Wunused-but-set-variable] int len; ^ root@wgd:~# ./adc 0.030085 A couple things to notice here. First is the warning I'm getting back from the compiler. This is because I'm using the -Wall option, which pretty much tells the compiler to use strict reporting of warnings. It is my belief that one should always at least use the -Wall compiler flag. Then we should treat these warning as if they're errors, and correct them. Which( and yeah I hate explanations like this too ) I'm ignoring for this one situation. Basically "len" is a return value from read() which can let us know how many bytes were read out of the file, and we absolutely should test this value for a non negative number. Then act on negative numbers as an error, which could be done a few different ways. For this demo, I was not sure in a pinch what would be a better way to handle that potential error. Mostly because I know these values will always be 0-4095, unless there is a problem with the hardware. At which point we're done anyhow( probably a blown processor ). One way to deal with this kind of error, would be to use perror() followed by exit(errno) in an if block. But at this point I'm fairly confident the system would not be running anyway . . .However, the values coming out of the ADC module should always be 1-4 characters, so how do we test for no characters ? NULL, but if we're reading out a NULL, how did our code make it thus far anyhow ? Additionally, if we're reading more than 4 character . . .Not only do I believe this to be impossible because of the way I wrote this code, but if it were somehow possible. We'd have a buffer overrun. Which may be a very good idea to test for. If for nothing else, good practice ? You decide. Secondly, the really low, but not absolute zero value. Well, I've come to realize that when working with circuits of this nature, that can not tied to ground, or pulled high. You're basically "floating" but close to a low, or a high . . . again, I'm not exactly an EE, so maybe someone who cares to can elaborate further. But I always think of this sort of situation as induced parasitic voltages from the circuitry. I'm definitely all ears if someone has a better explanation, or insight into this. . .But I've tested these readings against a volt meter, and am reasonably happy that I'm "close enough" for my own purposes. Someday, perhaps I'll buy a USB computer style Oscilloscope, in hopes to enlighten myself further..
  7. Read from a DS18B20 temperature sensor Again, very simple code to read from a device, and put that read information out to stdout. In this case, reading from a 1-wire DS18B20 sensor. The pin used is unimportant, so long as that pin is configurable as gpio, and is not already in use by another device. 1-wire is one of the simpler sensors to connect to a beaglebone, and can be plugged directly into one of the two headers on the beaglebone using jumper wires. You need power(3v3), ground, and a gpio pin connected. See the DS18B20 datasheet to determine which pin on the sensor is used for what purpose. As for setup in Linux for this sensor. You can search the web for a guide as to how to do this manually from the cmdline, or you can use a device tree overlay. I used this overlay file as a template, then modified the pin information to reflect the pin I needed to use. https://github.com/beagleboard/bb.org-overlays/blob/master/src/arm/BB-W1-P9.12-00A0.dts C code: #include <stdio.h> #include <dirent.h> #include <string.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> int read_DS18B20(void) { DIR *dir; struct dirent *dirent; char dev[16]; char devPath[128]; char buf[80]; char tmpData[6]; char path[] = "/sys/bus/w1/devices"; ssize_t nread; dir = opendir(path); if(dir == NULL){ perror("/sys/bus/w1/devices"); exit(errno); } while((dirent = readdir(dir))){ if (dirent->d_type == DT_LNK && strstr(dirent->d_name, "28-") != NULL){ strcpy(dev, dirent->d_name); } } (void)closedir(dir); sprintf(devPath, "%s/%s/w1_slave", path, dev); int fd = open(devPath, O_RDONLY); if (fd == -1){ perror ("/sys/bus/w1/devices/28-*/w1_slave"); exit(errno); } long tempC = 0; nread = read(fd, buf, 80); if(nread > 0){ strncpy(tmpData, strstr(buf, "t=") + 2, 5); tempC = strtol(tmpData, NULL, 10); } close(fd); return tempC; } int main (void) { float temp = read_DS18B20() * (1 / 1000.0); printf("Temp: %.3f C \n", temp); return 0; } Output: root@wgd:~/# gcc -Wall -o read_ds18b20 read_ds18b20.c root@wgd:~/# ./read_ds18b20 Temp: 25.312 C
  8. So, this is partly for me, and partly for others who need a refresher, or just do not know how. But I will be making several post here over time on how to write very simply code, to do one thing, or another. These, used in conjunction with a shell script could be very useful / flexible. After several long talks with many people, including some here on these very forums. I've decided that using C, to communicate with hardware, or hardware interfaces is best as can be for many situations. However, when you need to run several tools all at once, and have output formatted in some fashion, or easily modified. Shell scripts are very good at that sort of thing. Read from a real-time clock This post I will make about reading from a real-time clock. I spent hours messing around code related to I2C communications, and could never get exactly what I wanted. Plus, I wanted something that output date / time that looked very similar to the date Linux command. This could definitely been done using a shell script, but code size would probably be a lot larger. Additionally, a shell script would very likely be a lot slower, as with a script, one would have to be calling external cmdline tools to perform various operations. This example code is very fast, and prints to screen immediately after issuing the command. Since this command is very simple, and only prints the formatted date / time to screen. This could very easily be called from a shell script, and formatted further if need be. The real-time clock I'm using for this demonstration is a Maxim DS3232 real-time clock which is very accurate, and also very expensive compared to other real-time clocks. At $7 + US each, it's not cheap. I also had to write my own device tree overlay for this RTC, which strictly speaking is not necessary. One can set the device up from the command line manually as demonstrated for many different RTC's on the web. In fact, all the device tree overlay that I wrote does, is set all this automatically up at boot. As far as teh actual overlay it's self. All I did was modify an existing overlay from the "official" bb-overlays repo on github. https://github.com/beagleboard/bb.org-overlays/blob/master/src/arm/BB-RTC-01-00A0.dts To look something like this: /* * Copyright (C) 2015 Robert Nelson <robertcnelson@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /dts-v1/; /plugin/; #include <dt-bindings/board/am335x-bbw-bbb-base.h> #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/pinctrl/am33xx.h> / { compatible = "ti,beaglebone", "ti,beaglebone-black", "ti,beaglebone-green"; /* identification */ part-number = "BB-RTC-01"; version = "00A0"; fragment@2 { target = <&i2c2>; __overlay__ { status = "okay"; /* shut up DTC warnings */ #address-cells = <1>; #size-cells = <0>; /* MCP79410 RTC module */ rtc@68 { compatible = "maxim,ds3232"; reg = <0x68>; }; }; }; }; On our cape, the RTC is on bus I2C-2, which is already enabled by default for capemgr. The rest of the above just means that status is okay(load the device ), the kernel module to load is called "ds3232", and the device address on the bus is 0x68. Now on to the actual C code for reading from /dev/rtc1: #include <stdio.h> #include <stdlib.h> #include <linux/rtc.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> void display_date_time(void) { struct rtc_time rtc_tm; int fd = open("/dev/rtc1", O_RDONLY); if(fd == -1){ perror("/dev/rtc"); exit(errno); } /* Read the RTC time/date */ int retval = ioctl(fd, RTC_RD_TIME, &rtc_tm); if (retval == -1) { perror("ioctl"); exit(errno); } int d = rtc_tm.tm_mday; int m = rtc_tm.tm_mon + 1; int y = rtc_tm.tm_year + 1900; const char *wdays[] = {"Sun","Mon","Tues","Wed","Thur","Fri","Sat"}; const char *mnths[] = {"Jan","Feb","Mar","Apr","May","June","July","Aug","Sept","Oct","Nov","Dec"}; int wday = (d += m < 3 ? y-- : y - 2, 23*m/9 + d + 4 + y/4- y/100 + y/400)%7; fprintf(stdout, "%s %s %02d %02d:%02d:%02d %d UTC\n", wdays[wday], mnths[rtc_tm.tm_mon], rtc_tm.tm_mday, rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec, y); } int main( int argc, char **argv ) { display_date_time(); return 0; } As one can see, most of this code is for formatting the output in a specific way. In this case, the output will look exactly like the output one might expect to see after issuing the command "date". However, this output is fixed to output the date / time in the UTC time zone. As for one of the projects I'm using this in is for devices spread out all over the US, in 3 different time zones, and we do not care so much what the local time zone of that system so much, as much as knowing a given time "standard". e.g. if something fails, and we need to tell a customer what failed, and what time it failed, we can, Then if we need to convert that time to their time zone, easy. Notice that the read() is handled by ioctl(). . . Output: root@wgd:~/# gcc -Wall -o read_rtc read_rtc.c root@wgd:~/# ./read_rtc Tues May 09 23:08:49 2017 UTC
  9. So, almost everything I need is here. There only thing I'm missing is the non conductive M3 standoff washer( dont know what else to call them ), and the actual 1/4" screws that screw into the standoffs. Just playing around today, I took the main board tray piece of aluminum I have precut, and marked out the screw holes to mount it to the case frame. How I did this was cut a piece of cardboard exactly the same size as the piece of aluminum I had cut, put it into the case, pressed down on the cardboard until I got indentations form the mounting standoffs. Then I drill out the center of these indentations, and scribed onto aluminum with a fine point sharpie. I was not very happy with this result. So something I was thinking about the other day, and wulf reminded me of today, was to go find the standard for mini itx boards, and get the measurments from that. Gee, why didn't I think of that, which I did, just forgot . . .but i did find this: http://www.formfactors.org/developer/specs/mini_itx_spec_v1_1.pdf Perfect, so all I need now is daylight, and a bit of time. Then I'll revisit / re-approach this problem and get busy. I did take a few pictures, but they're still on the phone, and I CBF'd right now to upload them Perhaps later.
  10. Yeah after watching this video on gitlab: https://www.youtube.com/watch?v=PoBaY_rqeKA I've concluded for now, it's a bit more complex than I want. Granted . . . git it's self is fairly complex too, but gitlab would be something else I had to read up on, and keep up with, when all I really want is to feel all warm and fuzzy about my data being there when I need it. I may even think about making my backup strategy even simplier than what I'm currently think it should be now. May even forgo git all together, and just use rsync in conjunction with dd or tar. For 3 layers of redundancy. But I do liek the idea of versioning . . . a lot.
  11. It sounds interesting. Having a web based GUI could be useful, if that's what it is. Quite honestly though, and do keep in mind im by far not a git guru. I prefer to use tools of this nature form the command line. Honestly, I find using the tools much quicker from the command line. and this way keeps me in touch with how the tool is used. So if someday I wish to script something up, it should be trivial. Which is something else I've been doing a lot of. Which also lends a lot to readability for anyone who knows their way around a Linux system. So when working with others, like for me at work, I designed a complete monitoring system for production boards that anyone at work can read, and understand fairly quickly Even the boss, who is actually an old school embedded *NIX systems software developer. Anyway, for me, I'm not doing a local backup through git right now. But I have invested a considerable amount of time reading about it over the last year or so. So it's just a matter of time before I follow through. Then like I mentioned in a previous post. I'll have a completely separate partition, and mostly like a completely separate disk that I mount only when making backups. and probably using something like dd, to make 1:1 copies, or perhaps just using tar. So mount -> backup -> umount. This for me, will be very robust, and about as bullet proof as I would care to get. However, the system doing backups, for me would require a UPS, and possibly may just be a laptop for the built in battery power in case of power failures. Which is partly why I have not followed through with this yet. I want everythign runable during a power failure, and I have not had any of the required circutry, and possibly code designed yet. A friend of mine however( not wulf ) have been talking seriously about a design for a while now. Something he'll design in hardware, and something I'll design in software, assuming software will ever be needed.
  12. If someone loses data because their strategy is simplistic. Then they;'re not thinking how to do backup properly( not thinking the process through fully ), or they're doing something silly. You know the reflow oven wulf and I fiddled about with 5 is years ago ? I still have that code safely tucked away on a removable USB hard drive. My backup strategy ? About as simple as it gets. Manually copy files directly to my USB hard drive. Is it fool proof ? not by a long shot, but it works for me. Now all my beaglebone embedded Linux code, sits on a dedicated server, that's actually an Asus eee-pc, with a broken screen. But . . . william@eee-pc:~$ uptime 17:21:38 up 190 days, 22:13, 2 users, load average: 0.00, 0.01, 0.05 I'd say it's doing a pretty good job of what it's doing. Do I have a redundant copy of all my work ? In some cases, kind of, but not really. A lot of my important code is on a private git, on github. Now some of the other "important" stuff, is not redudant. But this code I classify as "important" is mostly experimental code, that I wrote while getting familiar with *something*. Be it CANBUS, sm_bus I2C stuff or whatever. By the way, the reason why that server only has a 190 uptime, Is because I shut it down while I was on vacation for a month . . .Unplugged it from power, and the network while I was at it too( thunderstorms . . .)
  13. This is where (possibly) git could come in handy, Lets say you need a specific file from a specific day for whatever reason. But anyhow, I personally, do not necessarily agree with the strategy you chose. But the point is here. we do not have to agree, because what you're getting is what *you* want. At minimum, what you think you want right now. Still, I urge you to look at your process, and think about it objectively. Which I think is what Rick was also trying to do. Finding an outside corner case where your backup strategy would fail you for a specific case. But let's say it does not fail, and does end up copying 1000 iterations of the same file slightly modified . . . this also may be less than desirable if that outside corner case just makes 1000 copies of the same file, with a different time stamp. or whatever. Really, what you need to think about is exactly what you want, and if your strategy is fulfilling what you want / need. For me, the difference between a file being saved when actual code differences have been made is a must, Meaning, I change a single line, I may want that change to stick and be persisted, For you, that may not be appropriate? So, for me personally. I think a local git hub may be exactly what I want, but also having a redundant copy of that local git would be necessary. For you . . . it may be different. Have I beat this horse a bit too much ?
  14. I wont stop, but I do not feel awesome I just know some stuff. I'm not looking for praise or anything like that. I just want people to start thinking in a way where they do not necessarily need to ask questions all the time. Meaning, yes, I ask questions too. I know a lot of things are possible, but I would just rather talk to, or ask someone whose had hands on experience about some things. A lot of times that makes me feel better about approaching a given situation. Anyway, I totally get that. One thing everyone should probably know about my frame of mind last night is that I had just finished off a bottle of wine so . . . I was not drunk or anything, but I was not exactly sober either. Had a good buzz I guess you could say. Still, this type of situation does bother me. It's like if you do not lead the "sheep" exactly where you want them to be, they just eat, sleep and sh** all in the same spot. Except, we, do not exactly need to lead these sheep anywhere. because their problem, is not ours. So when yo utry and help them out, and they just spit back in your face. I think any normal person is going ot get pissed off, and rightly so.
  15. It's really frustrating. Because I knew the answer to the question asked, and gave most of the answer already. I was just waiting to make sure the problem was as it seemed to be. For this, the person needed to give me some information, that should have already been provided . . .But since young people do not seen to know how to properly ask a question. It's all my fault. Yes, yes, yes, I do not know how to properly ask a question myself sometimes. We're all guilty. But you know, I can usually find myself a way out of a wet paper sack . . . We all need help, we're all also not all knowing . . . we're people, and we're all flawed. Great. But at least when I take the time to help you out. Show some appreciation at least . . .